Skip to content

Build and parse terms

Every piece of data a logic program handles is a term: an atom (bart), a number (42, 3.14), a variable (X), or a compound term, i.e. a functor applied to arguments (parent(homer, bart)). Lists, tuples and clauses are compound terms too. This guide shows how to make terms in Python and how to take them apart. For what terms are, see Terms and variables.

From Python values

The factories of tuprolog.core create each kind of term; those expecting terms convert Python values with to_term (strings become atoms, numbers numbers, lists logic lists, tuples logic tuples, booleans true/false):

>>> from tuprolog.core import atom, integer, logic_list, real, struct, to_term, var
>>> struct("parent", "homer", "bart")
parent(homer, bart)
>>> struct("point", 1, 2.5), integer(10**30), real("0.1")
(point(1, 2.5), 1000000000000000000000000000000, 0.1)
>>> logic_list(1, "a", [2, 3])
[1, a, [2, 3]]
>>> to_term(("a", "b", "c"))
(a, b, c)
>>> atom("Hello World")  # printed quoted, as Prolog syntax requires
'Hello World'

The conversion table lists every conversion.

From Prolog text

tuprolog.core.parsing parses Prolog syntax, with the standard operators. Printing a term (str, repr) writes most operators in canonical form ('+'(1, 2)); see Print terms for other styles:

>>> from tuprolog.core.parsing import parse_clause, parse_struct, parse_term
>>> term = parse_term("f(X, [1, 2 | T], 'hello world', 1 + 2 * 3)")
>>> term
f(X_..., [1, 2 | T_...], 'hello world', '+'(1, '*'(2, 3)))
>>> term[3].functor, term[3].args
('+', [1, '*'(2, 3)])
>>> parse_clause("grandparent(X, Z) :- parent(X, Y), parent(Y, Z)")
grandparent(X_..., Z_...) :- (parent(X_..., Y_...), parent(Y_..., Z_...))

Parsing an invalid text raises a tuprolog.errors.ParsingError, which is also a ValueError:

>>> from tuprolog.errors import ParsingError
>>> try:
...     parse_term("f(a")
... except ValueError as error:
...     print(isinstance(error, ParsingError), error.line, error.column)
True 1 4

Variables

Each var("X") call creates a distinct variable: printed names get a numeric suffix (X_0, X_1, ...) telling variables with the same name apart. Reuse the same Var object to refer to the same variable, e.g. in a rule's head and body:

>>> from tuprolog.core import rule, variables
>>> X = var("X")
>>> rule(struct("mortal", X), struct("human", X))
mortal(X_...) :- human(X_...)
>>> var("X") == var("X")
False
>>> A, B = variables("A", "B")

Within a parsed text, instead, equal names denote the same variable:

>>> t = parse_term("f(X, Y, X)")
>>> t[0] == t[2], t[0] == t[1]
(True, False)

Take terms apart

Terms are immutable trees. Inspect them with properties (functor, arity, args, is_atom, is_var, is_ground, ...) and indexing:

>>> t = parse_term("point(1, Y)")
>>> t.functor, t.arity, t.is_ground
('point', 2, False)
>>> t[0], t[1].is_var, t[1].name
(1, True, 'Y')
>>> [str(item) for item in parse_term("[a, b, c]")]
['a', 'b', 'c']

Convert terms back into Python values with to_python (terms without a Python counterpart, such as variables and compound terms, stay as they are):

>>> from tuprolog.core import to_python
>>> to_python(parse_term("[1, 2.5, abc, (x, y)]"))
[1, 2.5, 'abc', ('x', 'y')]

Apply substitutions

A substitution maps variables to terms. Build one by unifying two terms (see tuprolog.unify), then apply it:

>>> from tuprolog.unify import mgu
>>> pattern = parse_term("point(X, Y)")
>>> substitution = mgu(pattern, parse_term("point(1, 2)"))
>>> pattern.apply(substitution)
point(1, 2)
>>> mgu(parse_term("f(a)"), parse_term("f(b)")).is_failed  # no substitution makes them equal
True

str(term) writes Prolog syntax, with most operators in canonical form and variables with their numeric suffixes. Formatters (tuprolog.core.TermFormatter) offer other styles, e.g. with operators in infix notation and readable variable names:

>>> from tuprolog.core import pretty_expressions_formatter
>>> t = parse_term("X + 2 * Y")
>>> print(t)
'+'(X_..., '*'(2, Y_...))
>>> print(pretty_expressions_formatter().format(t))
X + 2 * Y

Custom operators

Operators let Prolog text use infix, prefix or postfix notation (1 + 2, - X, a :- b). Parse text with operators of your own by passing them, together with the default ones:

>>> from tuprolog.core.operators import DEFAULT_OPERATORS, XFX, operator, operator_set
>>> likes = operator_set(operator("likes", XFX, 700))
>>> parse_term("bart likes skateboarding", DEFAULT_OPERATORS + likes)
likes(bart, skateboarding)

See also