1% This file is part of the Attempto Parsing Engine (APE).
    2% Copyright 2008-2013, Attempto Group, University of Zurich (see http://attempto.ifi.uzh.ch).
    3%
    4% The Attempto Parsing Engine (APE) is free software: you can redistribute it and/or modify it
    5% under the terms of the GNU Lesser General Public License as published by the Free Software
    6% Foundation, either version 3 of the License, or (at your option) any later version.
    7%
    8% The Attempto Parsing Engine (APE) is distributed in the hope that it will be useful, but WITHOUT
    9% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
   10% PURPOSE. See the GNU Lesser General Public License for more details.
   11%
   12% You should have received a copy of the GNU Lesser General Public License along with the Attempto
   13% Parsing Engine (APE). If not, see http://www.gnu.org/licenses/.
   14
   15
   16:- module(prologfeatures, [
   17		resolve_features/1  % +Sourcefiles
   18	]).

Prolog-Features

This module reimplements a part of the ProFIT transformation that is introduced in [1]. It transforms Prolog programms that contain feature structures into plain Prolog programs. The following constructs are supported (the numbers refer to the paper):

(1) Super > [Sub1, ..., Subn].
(3) Sort intro [Feature1:Restr1, ..., Featuren:Restrn].
(4) < Sort
(5) Feature ! Value
(6) Term & Term
(9) TemplateName := TemplateValue
( ) @ Template
( ) Term or Term

[1] Gregor Erbach. ProFIT: Prolog with Features, Inheritance and Templates. In Proceedings of the seventh conference on European chapter of the Association for Computational Linguistics. Morgan Kaufmann Publishers Inc., 1995.

author
- Tobias Kuhn
version
- 2008-02-15 */
   47:- op(700, xfx, :=).   48:- op(695, xfx, intro).   49:- op(580, xfy, &).   50:- op(570, xfy, !).   51:- op(550, fx, @).   52:- op(550, fx, <).   53:- op(590, xfy, or).
 resolve_features(+Sourcefiles)
Reads the sourcefiles and transforms them by resolving the contained feature structures.
   60resolve_features(Sourcefiles) :-
   61    cleanup,
   62    read_sourcefiles(Sourcefiles),
   63    write_targetfiles.
 sort_features(+Sort, -Features)
Returns the list of features (without feature restrictions) for a certain sort. It fails if the sort does not have features.
   71:- dynamic(sort_feature/2).
 feature(+Feature, -Sort, -FeatureRestr)
The sort and the feature value are returned for a certain feature. This predicate might succeed several times.
   79:- dynamic(feature/3).
 subsort(?Subsort, ?Supersort)
Stores the subsort-supersort relations.
   86:- dynamic(subsort/2).
 template(+TemplateName, -Template)
Stores the templates. Several templates can have the same name.
   93:- dynamic(template/2).
 term(-Term)
Stores all the other terms that are not considered commands.
  100:- dynamic(term/1).
 error_occurred
Succeeds if an error occurred.
  107:- dynamic(error_occurred/0).
 cleanup
Retracts all the dynamic predicates.
  114cleanup :-
  115    retractall(sort_features(_,_)),
  116    retractall(feature(_,_,_)),
  117    retractall(subsort(_,_)),
  118    retractall(term(_)),
  119    retractall(template(_,_)),
  120    retractall(error_occurred).
 read_sourcefiles(+Sourcefiles)
Reads and processes the sourcefile(s).
  127read_sourcefiles([]).
  128
  129read_sourcefiles([File|Rest]) :-
  130    read_sourcefiles(File),
  131    read_sourcefiles(Rest).
  132
  133read_sourcefiles(File) :-
  134    \+ is_list(File),
  135    atom_concat(File, '.fit', FileC),
  136    open(FileC, read, In),
  137    write(user_error, 'Reading "'),
  138    write(user_error, FileC),
  139    write(user_error, '"\n'),
  140    assert(term('*** file ***'(File))),
  141    repeat,
  142      read_term(In, Term, [module(prologfeatures)]),
  143      store_term(Term),
  144      Term = end_of_file,
  145    close(In).
 store_term(+Term)
Stores the term in this module using the dynamic predicates.
  152store_term(end_of_file) :-
  153    !.
  154
  155store_term(Sort > Subsorts intro Features) :-
  156    atom(Sort),
  157    \+ sort_features(Sort, _),
  158    \+ subsort(_, Sort),
  159    ground(Features),
  160    Features = [_|_],
  161    prune_features(Features, FeaturesT),
  162    ground(Subsorts),
  163    Subsorts = [_|_],
  164    store_subsorts(Subsorts, Sort),
  165    !,
  166    assert(sort_features(Sort, FeaturesT)),
  167    store_features(Features, Sort).
  168
  169store_term(Sort > Subsorts intro Features) :-
  170    !,
  171    write_error_message('Invalid command', Sort > Subsorts intro Features).
  172
  173store_term(Sort intro Features) :-
  174    atom(Sort),
  175    \+ sort_features(Sort, _),
  176    ground(Features),
  177    Features = [_|_],
  178    prune_features(Features, FeaturesT),
  179    !,
  180    assert(sort_features(Sort,FeaturesT)),
  181    store_features(Features, Sort).
  182
  183store_term(Sort intro Features) :-
  184    !,
  185    write_error_message('Invalid command', Sort intro Features).
  186
  187store_term(Sort > Subsorts) :-
  188    atom(Sort),
  189    \+ subsort(_, Sort),
  190    ground(Subsorts),
  191    Subsorts = [_|_],
  192    store_subsorts(Subsorts, Sort),
  193    !.
  194
  195store_term(Sort > Subsorts) :-
  196    !,
  197    write_error_message('Invalid command', Sort > Subsorts).
  198
  199store_term(TemplateName := Template) :-
  200    atom(TemplateName),
  201    !,
  202    assert(template(TemplateName, Template)).
  203
  204store_term(TemplateName := Template) :-
  205    !,
  206    write_error_message('Invalid command', TemplateName := Template).
  207
  208store_term(Term) :-
  209    assert(term(Term)).
 prune_features(+FeaturesIn, -FeaturesOut)
Removes the feature-values and keeps only the feature-names. The input is checked for wellformedness.
  217prune_features([], []).
  218
  219prune_features([Feature|FeaturesRestIn], [Feature|FeaturesRestOut]) :-
  220    atom(Feature),
  221    \+ member(Feature, FeaturesRestIn),
  222    \+ member(Feature:_, FeaturesRestIn),
  223    !,
  224    prune_features(FeaturesRestIn, FeaturesRestOut).
  225
  226prune_features([Feature:FeatureRestr|FeaturesRestIn], [Feature|FeaturesRestOut]) :-
  227    atom(Feature),
  228    atom(FeatureRestr),
  229    \+ member(Feature, FeaturesRestIn),
  230    \+ member(Feature:_, FeaturesRestIn),
  231    !,
  232    prune_features(FeaturesRestIn, FeaturesRestOut).
  233
  234prune_features([Feature|_], _) :-
  235    write_error_message('Invalid or duplicated feature', Feature),
  236    fail.
 store_features(+Features, +Sort)
Stores the features using the dynamic feature/3 predicate.
  243store_features([], _).
  244
  245store_features([Feature:FeatureRestr|FeaturesRest], Sort) :-
  246    !,
  247    assert(feature(Feature, Sort, FeatureRestr)),
  248    store_features(FeaturesRest, Sort).
  249
  250store_features([Feature|FeaturesRest], Sort) :-
  251    assert(feature(Feature, Sort, '')),
  252    store_features(FeaturesRest, Sort).
 store_subsorts(+Subsorts, +Supersort)
Stores the subsort-supersort relations using the subsort/2 predicate.
  259store_subsorts([], _).
  260
  261store_subsorts([Subsort|_], Sort) :-
  262    is_subsort_of(Sort, Subsort),
  263    !,
  264    write_error_message('Cycle introduced in sort hierarchy', Sort > Subsort),
  265    fail.
  266
  267store_subsorts([Subsort|SubsortsRest], Sort) :-
  268    assert(subsort(Subsort, Sort)),
  269    store_subsorts(SubsortsRest, Sort).
 write_targetfiles
Transforms the input terms and writes the target files. If an error occurred in an ealier stage, the execution is aborted.
  277write_targetfiles :-
  278    error_occurred,
  279    !,
  280    write(user_error, 'EXECUTION ABORTED.\n').
  281
  282write_targetfiles :-
  283    term(Term),
  284    process_term(Term),
  285    fail.
  286
  287write_targetfiles :-
  288    told.
 process_term(+Term)
Transforms the term and writes the translated term(s) to the file.
  295process_term('*** file ***'(FileName)) :-
  296    !,
  297    told,
  298    atom_concat(FileName, '.plp', OutFile),
  299    write(user_error, 'Writing "'),
  300    write(user_error, OutFile),
  301    write(user_error, '"\n'),
  302    tell(OutFile).
  303
  304process_term(Term) :-
  305    findall(TermT, transform(Term, TermT, []), TermsT),
  306    TermsT = [_|_],  % must have at least one element
  307    !,
  308    write_terms(TermsT).
  309
  310process_term(Term) :-
  311    write_error_message('Invalid term', Term).
 write_terms(+TermList)
Writes each of the terms to the current output device.
  318write_terms([]).
  319
  320write_terms([Term|TermsRest]) :-
  321    numbervars(Term, 0, _),
  322    write_term(Term, [character_escapes(true), quoted(true), numbervars(true), module(prologfeatures)]),
  323    write('.\n'),
  324    write_terms(TermsRest).
 transform(+TermIn, -TermOut, +Templates)
Transforms a term with feature structure into a term without. This predicate can succeed several times. The argument Templates stores the path of visited templates in order to detect loops.
  333transform(Var, Var, _) :-
  334    var(Var),
  335    !.
  336
  337transform([], [], _) :-
  338	!.
  339
  340transform([H1|T1], [H2|T2], Templates) :-
  341	!,
  342	transform(H1, H2, Templates),
  343	transform(T1, T2, Templates).
  344
  345transform(Term, Term, _) :-
  346	Term =.. [Term],
  347	!.
  348
  349transform(Feature ! Value, Term, Templates) :-
  350    !,
  351    atom(Feature),
  352    build_feature(Feature, Value, Term, Templates).
  353
  354transform(< Sort, Term, _) :-
  355    !,
  356    build_sort(Sort, _, Term, _, _).
  357
  358transform(@ TemplateName, Term, Templates) :-
  359    \+ member(TemplateName, Templates),
  360    !,
  361    template(TemplateName, Template),
  362    transform(Template, Term, [TemplateName|Templates]).
  363
  364transform(@ TemplateName, _, Templates) :-
  365    !,
  366    write_error_message('Cyclic template definition', [TemplateName|Templates]),
  367    fail.
  368
  369transform(T1 & T2, Term, Templates) :-
  370    !,
  371    transform(T1, Term, Templates),
  372    transform(T2, Term, Templates).
  373
  374transform(T1 or T2, Term, Templates) :-
  375    !,
  376    (
  377    	transform(T1, Term, Templates)
  378    ;
  379    	transform(T2, Term, Templates)
  380    ).
  381
  382transform(Term1, Term2, Templates) :-
  383	!,
  384	Term1 =.. List1,
  385	transform(List1, List2, Templates),
  386	Term2 =.. List2.
 build_feature(+Feature, +Value, -Term, +Templates)
Transforms a feature. This predicate can succeed several times. The argument Templates stores the path of visited templates in order to detect loops.
  394build_feature(Feature, Value, Term, Templates) :-
  395    feature(Feature, Sort, FeatureRestr),
  396    build_sort(Sort, _, Term, Feature, FeatureVar),
  397    transform(Value, ValueTerm, Templates),
  398    build_sort(FeatureRestr, _, ValueTerm, _, _),
  399    FeatureVar = ValueTerm.
 build_sort(?Sort, +Content, -Term, +Feature, -FeatureVar)
Transforms a sort. This predicate can succeed several times.
  406build_sort(Sort, _, _, _, _) :-
  407    Sort == '',
  408    !.
  409
  410build_sort(Sort, Content, Term, Feature, FeatureVar) :-
  411    subsort(Sort, top),
  412    build_sort_args(1, Sort, Content, Args, Feature, FeatureVar),
  413    atom_concat('$', Sort, SortT),
  414    Term =.. [SortT,_|Args].
  415
  416build_sort(Sort, Content, Term, Feature, FeatureVar) :-
  417    subsort(Sort, SuperSort),
  418    SuperSort \= top,
  419    build_sort_args(1, Sort, Content, Args, Feature, FeatureVar),
  420    atom_concat('$', Sort, SortT),
  421    TempTerm =.. [SortT|Args],
  422    build_sort(SuperSort, TempTerm, Term, _, _).
 build_sort_args(+StepNumber, +Sort, +Content, -Args, +Feature, -FeatureVar)
Builds the arguments for the transformed term.
  429build_sort_args(1, Sort, Content, [Content|Args], Feature, FeatureVar) :-
  430    subsort(_, Sort),
  431    !,
  432    build_sort_args(2, Sort, Content, Args, Feature, FeatureVar).
  433
  434build_sort_args(1, Sort, Content, Args, Feature, FeatureVar) :-
  435    build_sort_args(2, Sort, Content, Args, Feature, FeatureVar).
  436
  437build_sort_args(2, Sort, _, FeatureVarList, Feature, FeatureVar) :-
  438    sort_features(Sort, Features),
  439    !,
  440    create_featurevarlist(Features, FeatureVarList, Feature, FeatureVar).
  441
  442build_sort_args(2, _, _, [], _, _).
 create_featurevarlist(+FeatureList, -FeatureVarList, +Feature, -FeatureVar)
Transforms a list of features (i.e. atoms) into a list of variables. The argument FeatureVar returns the variable for Feature.
  450create_featurevarlist([], [], _, _).
  451
  452create_featurevarlist([Feature|FeaturesRest], [FeatureVar|FeatureVarListRest], Feature, FeatureVar) :-
  453    !,
  454    create_featurevarlist(FeaturesRest, FeatureVarListRest, Feature, FeatureVar).
  455
  456create_featurevarlist([_|FeaturesRest], [_|FeatureVarListRest], Feature, FeatureVar) :-
  457    !,
  458    create_featurevarlist(FeaturesRest, FeatureVarListRest, Feature, FeatureVar).
 is_subsort_of(+Subsort, +Sort)
Succeeds if Subsort is a direct or indirect subsort of Sort.
  465is_subsort_of(Sort, Sort).
  466
  467is_subsort_of(Subsort, Sort) :-
  468    subsort(Subsort, T),
  469    is_subsort_of(T, Sort).
 write_error_message(+Message, +Term)
Writes the error message to the error device.
  476write_error_message(Message, Term) :-
  477    assert(error_occurred),
  478    write(user_error, 'ERROR: '),
  479    write(user_error, Message),
  480    write(user_error, ': '),
  481    write_term(user_error, Term, [module(prologfeatures), quoted(true)]),
  482    write(user_error, '\n')