:- module(owlace_dcg, [owl_ace/2]). /** OWL ACE Definite Clause Grammar Converts a sentence in a fragment of ACE into an axiom in a syntactic fragment of OWL 2 without data-valued properties and some object property axioms. And vice versa. _| Note that in order to make OWL expressions easier to read, we have dropped 'Object' from the names of the class description constructors. I.e. 'ComplementOf' is really 'ObjectComplementOf'. |_ _| Note that this grammar is not connected to a lexicon. To keep things simple, we only support a small number of content words ('John', 'man', 'thing', 'like'). |_ @author Kaarel Kaljurand @version 2007-03-10 */ %% owl_ace(+OWL:term, -ACE:list) is nondet. %% owl_ace(-OWL:term, +ACE:list) is nondet. % % @param OWL is an OWL axiom in the OWL functional-style syntax (Prolog notation) % @param ACE is an ACE sentence represented as a list of atoms % % An example of verbalizing an OWL axiom as an ACE sentence. % %== %?- owl_ace('SubClassOf'(man, 'HasValue'('InverseObjectProperty'(like), 'John')), ACE). % %ACE = ['Every', man, is, liked, by, 'John', '.'] ; % %No %== % % An example of using ACE to express OWL axioms. % Due to coordination ambiguity there can be several solutions. The solution % that is compatible with the ACE interpretation rules (i.e. the relative clause % modifies to the closest preceding noun) is always the first solution. % %== %?- owl_ace(OWL, ['Every', 'man', 'is', 'a', 'thing', 'that', 'likes', 'a', 'thing', 'that', 'is', 'John', 'or', 'that', 'is', 'John', '.']). % %OWL = 'SubClassOf'(man, 'IntersectionOf'('owl:Thing', 'SomeValuesFrom'(like, 'IntersectionOf'('owl:Thing', 'UnionOf'('OneOf'('John'), 'OneOf'('John')))))) ; % %OWL = 'SubClassOf'(man, 'IntersectionOf'('owl:Thing', 'UnionOf'('SomeValuesFrom'(like, 'IntersectionOf'('owl:Thing', 'OneOf'('John'))), 'OneOf'('John')))) ; % %No %== % owl_ace(OWL, ACE) :- ip(OWL, ACE, []). % % SYNTAX % ip(SubClassOf) --> nps(Y, SubClassOf), ibar(num=sg, Y), ['.']. ibar(num=sg, C2) --> auxc(C1, C2), cop(C1). ibar(Num, C2) --> auxv(Num, Neg, Part, C1, C2), vp(Num, Neg, Part, C1). cop(C) --> [a], n(num=sg, C). cop(C) --> propn(C). cop('IntersectionOf'(C1, C2)) --> [a], n(num=sg, C1), relcoord(num=sg, C2). vp(Num, Neg, Part, Restr) --> tv(Num, Neg, Part, R), npo(R, Restr). nps(C, 'SubClassOf'(OneOf, C)) --> propn(OneOf). %nps(C, 'ClassAssertion'(Individual, C)) --> propn('OneOf'(Individual)). nps(Y, Out) --> dets(Num, X, Y, Out), n(Num, X). nps(Y, Out) --> dets(Num, 'IntersectionOf'(X, Restr), Y, Out), n(Num, X), relcoord(Num, Restr). npo(R, 'SomeValuesFrom'(R, OneOf)) --> propn(OneOf). %npo(R, 'HasValue'(R, Individual)) --> propn('OneOf'(Individual)). npo(R, 'ExistsSelf'(R)) --> [itself]. npo(R, Restr) --> detg(Num, R, C, Restr), n(Num, C). npo(R, Restr) --> detg(Num, R, 'IntersectionOf'(C, RelCl), Restr), n(Num, C), relcoord(Num, RelCl). relcoord(Num, Coord) --> relcoord_1(Num, X), relcoord_tail(Num, X, Coord). relcoord_tail(Num, X, Coord) --> comma_and(X, Y, Coord), relcoord(Num, Y). relcoord_tail(_, X, X) --> []. relcoord_1(Num, Coord) --> relcoord_2(Num, X), relcoord_1_tail(Num, X, Coord). relcoord_1_tail(Num, X, Coord) --> or(X, Y, Coord), relcoord_1(Num, Y). relcoord_1_tail(_, X, X) --> []. relcoord_2(Num, Coord) --> rel(Num, X), relcoord_2_tail(Num, X, Coord). relcoord_2_tail(Num, X, Coord) --> and(X, Y, Coord), relcoord_2(Num, Y). relcoord_2_tail(_, X, X) --> []. rel(Num, X) --> relpro, ibar(Num, X). % % FORMAL LEXICON % dets(num=sg, C1, C2, 'SubClassOf'(C1, C2)) --> ['Every']. % Syntactic sugar 'no' could be implemented like this: % dets(num=sg, C1, C2, 'SubClassOf'(C1, 'ComplementOf'(C2))) --> ['No']. detg(num=sg, R, C, 'SomeValuesFrom'(R, C)) --> [a]. detg(num=sg, R, C, 'AllValuesFrom'(R, C)) --> [nothing, but, a]. detg(num=pl, R, C, 'MinCardinality'(Number, R, C)) --> [at, least], positivenumber(Number). detg(num=pl, R, C, 'MaxCardinality'(Number, R, C)) --> [at, most], positivenumber(Number). detg(num=pl, R, C, 'ExactCardinality'(Number, R, C)) --> [exactly], positivenumber(Number). auxc(C, C) --> [is]. auxc(C, 'ComplementOf'(C)) --> [is, not]. auxv(_, neg=no, vbn=no, C, C) --> []. auxv(_, neg=no, vbn=yes, C, C) --> [is]. auxv(num=sg, neg=yes, vbn=no, C, 'ComplementOf'(C)) --> [does, not]. auxv(num=pl, neg=yes, vbn=no, C, 'ComplementOf'(C)) --> [do, not]. auxv(num=sg, neg=yes, vbn=yes, C, 'ComplementOf'(C)) --> [is, not]. auxv(num=pl, neg=yes, vbn=yes, C, 'ComplementOf'(C)) --> [are, not]. relpro --> [that]. comma_and(C1, C2, 'IntersectionOf'(C1, C2)) --> [',', and]. and(C1, C2, 'IntersectionOf'(C1, C2)) --> [and]. or(C1, C2, 'UnionOf'(C1, C2)) --> [or]. % General number rule (note that we don't support 0): % positivenumber --> [PositiveNumber], { between(1, infinite, PositiveNumber) }. positivenumber(2) --> [2]. % % CONTENT LEXICON % % Propernames can come only in one form. propn('OneOf'('John')) --> ['John']. % Actually, propernames do not have to be included in the lexicon. % We know from the context (i.e. missing determiner) % what is a propername. Furthermore, propernames do not need % morphological synthesis. % propn('OneOf'(ProperName)) --> [ProperName]. % For nouns we have to describe 2 forms: {num=sg, num=pl} n(num=sg, 'owl:Thing') --> [thing]. n(num=pl, 'owl:Thing') --> [things]. n(num=sg, man) --> [man]. n(num=pl, man) --> [men]. % For verbs we have to describe 8 forms: % {neg=yes, neg=no} * {num=sg, num=pl} * {vbn=no, vbn=yes} % likes % like % does not like % do not like % is liked by % are liked by % is not liked by % are not liked by % Note that the (external) lexicon has to provide only 3 forms: % finite form, infinitive, and past participle (e.g. likes, like, liked). tv(num=sg, neg=no, vbn=no, like) --> [likes]. tv(num=pl, neg=no, vbn=no, like) --> [like]. tv(num=sg, neg=yes, vbn=no, like) --> [like]. tv(num=pl, neg=yes, vbn=no, like) --> [like]. tv(_, _, vbn=yes, 'InverseObjectProperty'(like)) --> [liked, by]. %% pp % % Pritty printer. Generates all sentences allowed in the language. % There is a restriction on the sentence length to get a finite output. % % Usage on the Unix commandline (where 'swipl' is a SWI-Prolog executable): % %== % echo "[owlace_dcg]. owlace_dcg:pp." | swipl %== pp :- forall( ( init_sentence(ACE), owl_ace(OWL, ACE) ), ( concat_atom(ACE, ' ', ACEAtom), format('~w :: ~w~n', [ACEAtom, OWL]) ) ). init_sentence(ACE) :- between(4, 12, Int), length(ACE, Int).