Previous Up Next

8.17 Term expansion

8.17.1 Definite clause grammars

Definite clause grammars are a useful notation to express grammar rules. However the ISO reference does not include them, so they should be considered as a system dependent feature. Definite clause grammars are an extension of context-free grammars. A grammar rule is of the form:

head --> body.

--> is a predefined infix operator (section 8.14.10).

Here are some features of definite clause grammars:

A grammar rule is nothing but a “syntactic sugar” for a Prolog clause. Each grammar rule accepts as input a list of terminals (tokens), parses a prefix of this list and gives as output the rest of this list (possibly enlarged). This rest is generally parsed later. So, each a grammar rule is translated into a Prolog clause that explicitly the manages the list. Two arguments are then added: the input list (Start) and the output list (End). For instance:

p --> q.

is translated into:

p(Start, End) :- q(Start, End).

Extra arguments can be provided and the body of the rule can contain several non-terminals. Example:

p(X, Y) -->
        q(X),
        r(X, Y),
        s(Y).

is translated into:

p(X, Y, Start, End) :-
        q(X, Start, A),
        r(X, Y, A, B),
        s(Y, B, End).

Terminals are translated using unification:

assign(X,Y) --> left(X), [:=], right(Y), [;].

is translated into:

assign(X,Y,Start,End) :-
        left(X, Start, A),
        A=[:=|B],
        right(Y, B, C),
        C=[;|End].

Terminals appearing on the left-hand side of a rule are connected to the output argument of the head.

It is possible to include a call to a prolog predicate enclosing it in curly brackets (to distinguish them from non-terminals):

assign(X,Y) --> left(X), [:=], right(Y0), {Y is Y0 }, [;].

is translated into:

assign(X,Y,Start,End) :-
        left(X, Start, A),
        A=[:=|B],
        right(Y0, B, C),
        Y is Y0,
        C=[;|End].

Cut, disjunction and if-then(-else) are translated literally (and do not need to be enclosed in curly brackets).

8.17.2 expand_term/2, term_expansion/2

Templates

expand_term(?term, ?term)
term_expansion(?term, ?term)

Description

expand_term(Term1, Term2) succeeds if Term2 is a transformation of Term1. The transformation steps are as follows:

term_expansion(Term1, Term2) is a hook predicate allowing the user to define a specific transformation. If this predicate succeeds, Term2 is compiled instead of Term1. Term2 can be a list, in which case, all terms of the list are compiled.

The GNU Prolog compiler (section 4.4) automatically calls expand_term/2 on each Term1 read in. In version prior to 1.6.0, only DCG transformation was done by the compiler (i.e. term_expansion/2 could not be used). Since 1.6.0, term_expansion/2 is supported. Due to separate compilation, some pl2wam options and directives can be necessary (section 4.4.3). Basically, it is necesary to inform the Prolog to WAM compiler that a predicate must be embeded in its internal state (so it can be executed during term expansion). By default, only term_expansion/2 clauses are implicitely embeded. All other predicates (directly or indirectly) called by term_expansion/2 must be declared as embeded. This can be done with compiler_modes/1 directive. Here is an example of term_expansion calling an auxiliary predicate mk_postfix/3:

:- compiler_mode(embed).   % use embed_compile to also compile this part

mk_postfix(E, NextP, P1) :-
        callable(E), !,
        functor(E, F, 2), !,
        arg(1, E, E1),
        arg(2, E, E2),
        mk_postfix(E1, P2, P1),
        mk_postfix(E2, [F|NextP], P2).
mk_postfix(E, NextP, [E|NextP]) :-
        number(E), !.

term_expansion((expr(E)), (eval(E, R) :- eval_postfix(P, [], R))) :-
        mk_postfix(E, [], P).

:- compiler_mode(default).

expr(12 + 3*8).             % expressions to be converted
expr((6-2-1) * 8).

eval_postfix([], [R], R).   % postfix expression evaluation
eval_postfix([X|P], S, R) :-
        number(X), !,
        eval_postfix(P, [X|S], R).
eval_postfix([Op|P], [Y, X|S], R) :-
        Expr =.. [Op, X, Y],
        Z is Expr,
        eval_postfix(P, [Z|S], R).

The following clauses will then be obtained by expansion and compiled (in addition to the predicate eval_postif/2):

eval(12 + 3*8, A) :-
        eval_postfix([12, 3, 8, *, +], [], A).
eval((6-2-1) * 8, A) :-
        eval_postfix([6, 2, -, 1, -, 8, *], [], A).

Errors

Any error which can occur when invoking call(term_expansion(Term1, Term2)).

Portability

GNU Prolog predicate.

8.17.3 phrase/3, phrase/2

Templates

phrase(?term, ?list, ?list)
phrase(?term, ?list)

Description

phrase(Phrase, List, Remainder) succeeds if the list List is in the language defined by the grammar rule body Phrase. Remainder is what remains of the list after a phrase has been found.

phrase(Phrase, List) is equivalent to phrase(Phrase, List, []).

Errors

Phrase is a variableinstantiation_error
Phrase is neither a variable nor a callable termtype_error(callable, Phrase)
List is neither a list nor a partial listtype_error(list, List)
Remainder is neither a list nor a partial listtype_error(list, Remainder)

Portability

GNU Prolog predicates.

Copyright (C) 1999-2025 Daniel Diaz Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved. More about the copyright
Previous Up Next