% Satzende-Erkenner und Tokenizer   Prolog-Kurs WS 2011/12, H.Leiß, CIS

read_sentence(File,Atomlist) :-
        % Fall 1: File ist offen 
        current_stream(File,read,Stream),!, 
        read_sentence(Stream,Sentence,[]),
        stringToAtoms(Sentence,Atomlist).
read_sentence(File,Atomlist) :- 
        % Fall 2: sonst
        open(File,read,Stream),
        read_sentence(Stream,Sentence,[]),
        stringToAtoms(Sentence,Atomlist).

eingabe :-
        write('Beende die Eingabe mit <.|?><Return>'),
        nl,read_sentence(user,Sentence,[]),
        stringToAtoms(Sentence,Atomlist),
        nl,writeq(Atomlist).

read_sentence(Stream,Sentence,Seen) :-
        get0(Stream,Char),!,
        read_sentence(Stream,Sentence,Char,Seen).
        
% read_sentence(+Strom,-Sent.,+LastChar,+CharsRev)
read_sentence(Stream,Sentence,Char,Seen) :-       
        dateiende(Char),
        !,reverse(Seen,Sentence),
        name(Atom,Sentence),nl,write(Atom), % Echo
        nl,close(Stream),
        nl,write('End of File reached; Stream closed.').
read_sentence(Stream,Sent,Char,[Pkt,Zif|Seen]) :-
        ordinalzahlende([Zif,Pkt,Char]),!,
        read_sentence(Stream,Sent,[Char,Pkt,Zif|Seen]).
read_sentence(Stream,Sent,Char,[R|Seen]) :-
        abkuerzungsendeRev([Char,R|Seen]),
        !, read_sentence(Stream,Sent,[Char,R|Seen]).
read_sentence(Stream,Sentence,Char,[C|Seen]) :-
        satzende([C,Char]), % s.u.
        !, % Satzende gefunden, Satz ausgeben:
        reverse([C|Seen],Sentence).
read_sentence(Stream,Sentence,Char,Seen) :-
        read_sentence(Stream,Sentence,[Char|Seen]).

stringToAtoms(String,Atomlist) :-
    tokenize(String,[],[],Atomlist).
tokenize([T|String],[],Rs,Ts) :-            
    (leerZeichen(T); zeilenende(T)), 
    % fuehrende Leerzeichen,Zeilenumbruch weglassen
    !,tokenize(String,[],Rs,Ts).     
tokenize([T|String],[],Rs,Ts) :-
    sonderzeichen(Atom,T),  % eigenes Token
    !,tokenize(String,[],[Atom|Rs],Ts).
tokenize([C|String],[],Rs,Ts) :- % Wortanfang!
    !,tokenize(String,[C],Rs,Ts).% sammle Zeichen 
tokenize([N,L|String],[T|Cs],Rs,Ts) :-    
    worttrennung([T,N]),leerZeichen(L),  
    % Leerzeichen am Zeilenanfang beseitigen:
    !, tokenize([N|String],[T|Cs],Rs,Ts).
tokenize([N|String],[T|Cs],Rs,Ts) :-      
    worttrennung([T,N]),
    % Trennzeichen+Zeilenumbruch beseitigen:
    !, tokenize(String,Cs,Rs,Ts).
tokenize([T|String],R,Rs,Ts) :-           
    zeilenende(T),
    % Sonstige Zeilenenden sind Tokengrenzen 
    !, tokenToAtom(R,Atom),
    tokenize(String,[],[Atom|Rs],Ts).

tokenize([T|String],R,Rs,Ts) :-           
    leerZeichen(T),
    % Sonst: Tokenende erreicht, neues suchen
    !, tokenToAtom(R,Atom),
    tokenize(String,[],[Atom|Rs],Ts).

tokenize([S|String],R,Rs,Ts) :-
    punktZeichen(S),
    suffixAbkuerzung(Abkuerzung), %  Parkstr. 45
    reverse(Abkuerzung,RevAbk),
    append(RevAbk,_,[S|R]),
    !, tokenToAtom([S|R],Atom),
    tokenize(String,[],[Atom|Rs],Ts).
tokenize([S|String],R,Rs,Ts) :-         
    punktZeichen(S),
    abkuerzung(Abkuerzung),     %  z.T. 
    append(Praefix,[S|Suffix],Abkuerzung),
    reverse(Praefix,R),     
    append(Suffix,Rest,String),
    !, name(Atom,Abkuerzung),
    tokenize(Rest,[],[Atom|Rs],Ts).
tokenize([S|String],R,Rs,Ts) :-      
    sonderzeichen(SAtom,S), % Sonderzeichen als Token
    !, tokenToAtom(R,Atom), % gelesene Zeichen als Token
    tokenize(String,[],[SAtom,Atom|Rs],Ts).
tokenize([C|String],R,Rs,Ts) :-
    % sonst: erweitere das angefangene Token
    !,tokenize(String,[C|R],Rs,Ts).  
tokenize([],[C|Cs],Rs,Ts) :-
    !, tokenToAtom([C|Cs],Atom),
    reverse([Atom|Rs],Ts).
tokenize([],[],Rs,Ts) :- 
    reverse(Rs,Ts).

tokenToAtom(RevToken,Atom) :-                    
    reverse(RevToken,Token),
    name(Atom,Token).

dateiende(-1).           %  eof
zeilenende(10).          %  newline
worttrennung("-\n").     %  - newline
satzende(". ").  %  nicht: .Char 
satzende("? ").  satzende("! ").
satzende(": ").  satzende(".\n"). %  . newline 
satzende("?\n"). satzende("!\n").          
satzende(":\n").       
abkuerzungsendeRev([Char,R|Seen]) :-
        leerZeichen(Char),punktZeichen(R),
        (abkuerzung(Abk); suffixAbkuerzung(Abk)),
        reverse(Abk,RevAbk),
        append(RevAbk,_,[R|Seen]).    

suffixAbkuerzung("str.").
abkuerzung("Jan.").  abkuerzung("Feb.").
abkuerzung("Apr.").  abkuerzung("Aug.").
abkuerzung("Sept."). abkuerzung("Okt.").
abkuerzung("Nov.").  abkuerzung("Dez.").
abkuerzung("Dr.").   abkuerzung("Prof.").
abkuerzung("Fa.").   abkuerzung("Frl.").
abkuerzung("bzw.").  abkuerzung("etc.").
abkuerzung("usw.").  abkuerzung("vgl.").

abkuerzung("Nr.").   abkuerzung("a.a.O.").
abkuerzung("s.u.").  abkuerzung("m.M.").
abkuerzung("z.B.").  abkuerzung("z.T.").
abkuerzung([C,46]) :-  grossbuchstabe(C).  
grossbuchstabe(C) :-
        member(C,"ABCDEFGHIJKLMNOPQRSTUVWXYZ").
leerZeichen(32).        % name(' ',[32])  

% Ausser in Zahlausdruecken und Abkuerzungen 
% sollten folgende Zeichen eigene Token werden:
kommaZeichen(C) :- name(',',[C]).
punktZeichen(C) :- name('.',[C]).

sonderzeichen('.',46).  % name('.',[46]).  usw.
sonderzeichen(',',44).  sonderzeichen(';',59).
sonderzeichen(':',58).  sonderzeichen('!',33).
sonderzeichen('?',63).  sonderzeichen('"',34).  % bei Zitaten
sonderzeichen('(',40).  sonderzeichen(')',41).
ordinalzahlende([Ziffer,Punkt,Char]) :-
        ziffer(Ziffer),
        punktZeichen(Punkt),
        leerZeichen(Char).
ziffer(Z) :- 
        member(Z,"0123456789").
