Language Reference/Domains

Domains Sections
A domain section defines a set of domains in the current scope (see Interface, Class Declaration, and Class Implementation).

: domains -dot-term-list-opt

Domain Definitions
A domain definition defines a named domain in the current scope.

:  -opt = 

If the domain on the right hand side denotes an interface or a compound domain, then the defined domain is synonym (i.e. identical) to the type expression. Otherwise the defined domain becomes a subtype of the domain denoted by the type expression. Here a domain name  should be a.

There are certain places where you must use a domain name rather than a type expression:


 * as a declaration of a formal argument type;
 * as a type of a constant or a fact variable;
 * as a type in a list domain.

Domain Expressions
A domain expression denotes a type in a domain definition:

: one of           <TypeApplication>

Type Expressions
The full range of <DomainExpression> s can only be used in a domain definition. <TypeExpression> is a subset of these expressions that are used in other many other contexts.

<TypeExpression>: one of   <TypeName> <ListDomain> <TypeVariable> <ScopeTypeVariable> <TypeApplication>

Type Names
A type name is either an interface name or the name of a value domain. We use the term value domain to specify domains whose elements are (unchangeable). Here we can say that objects, belonging to domains correspondent to interface names, have mutable state and terms of any other domains are immutable. So actually value types are everything except object types. A type name (obviously) denotes the type corresponding to the name of an existing domain.

<TypeName>: one of   <InterfaceName> <DomainName> <ClassQualifiedDomainName>

<InterfaceName>: <LowercaseIdentifier>

<DomainName>: <LowercaseIdentifier>

<ClassQualifiedDomainName>: <ClassName>::<DomainName>

<ClassName>: <LowercaseIdentifier>

Here <InterfaceName> is an interface name, <DomainName> is a value domain name, and <ClassName> is a class name.

Compound Domains
Compound domains (also known as algebraic data types) are used to represent lists, trees, and other tree structured values. In its simple forms compound domains are used to represent structures and enumeration values. Compound domains can have a recursive definition. They can also be mutually/indirectly recursive.

<CompoundDomain>: <Alignment>-opt <FunctorAlternative>-semicolon-sep-list

<Alignment>: align <IntegralConstantExpression>

Here <IntegralConstantExpression> is an expression, which must be compile time evaluated to an integral value.

A compound domain declaration declares a list of functor alternatives with optional alignment. Alignment must be <vp>1</vp>, <vp>2</vp> or <vp>4</vp>.

If a compound domain consists of one functor alternative, then it is considered as structure and has representation, which is binary compatible with the appropriate structure in language C.

<FunctorAlternative>: <FunctorName> <FunctorName> ( <FormalArgument>-comma-sep-list-opt )

Here <FunctorName> is the name of a functor alternative it should be a.

<FormalArgument>: <TypeExpression> <ArgumentName>-opt

Here <ArgumentName> can be any. The compiler ignores it.

Compound domains have no subtype relations to any other domains.

If a domain is defined as being equal to a compound domain, then these two domains are synonym types rather than subtypes. Meaning that they are just two different names for the same type.

In the example above we used parenthesis after the null-ary function <vp>empty</vp>. Such parenthesis are optional in all situations, except in a domain definition consisting only of a single null-ary functor. In that case parenthesis are required to distinguish it from a synonym/subtype definition.

List Domains
List domains represent sequences of values of a certain domain. Thus, all elements in a <vp>T</vp> list must be of type <vp>T</vp>.

<ListDomain>: <TypeExpression> *

<vp>T*</vp> is the type of lists of <vp>T</vp> elements.

The following syntax is used for lists:

<ListExpression>: [ <Term>-comma-sep-list-opt ] [ <Term>-comma-sep-list | <Tail> ]

<Tail>: <Term>

Here <Tail> is a term which should have a value of the <ListDomain> type. Each <Term> should be of type.

Actually, lists are just compound domains with two functors: <vp>[]</vp> denoting the empty list and the mix-fix functor <vp>[HD|TL]</vp> denoting the list with head <vp>HD</vp> and tail <vp>TL</vp>. The head must be of the underlying element type, whereas the tail must be a list of relevant type.

Lists are however syntactically sugared.

<vp>[E1, E2, ..., En | L ]</vp> is shorthand for <vp>[E1 | [ E2 | [ ...[ En | L ]...] ] ]</vp>

<vp>[E1, E2, ..., En]</vp> is shorthand for <vp>[E1, E2, ..., En | [] ]</vp>, which in turn is shorthand for <vp>[E1 | [ E2 | [ ...[ En | [] ]...] ] ]</vp>.

Predicate Domains
Values of a predicate domain are predicates with the same "signature", i.e. the same argument and return types, the same flow pattern and the same (or stronger) predicate mode.

A predicate that returns a value is called a function, whereas a predicate that does not return a value is sometimes called an ordinary predicate, to stress that it is not a function.

<PredicateDomain>: ( <FormalArgument>-comma-sep-list-opt ) <ReturnArgument>-opt <PredicateModeAndFlow>-list-opt <CallingConvention>-opt

<FormalArgument>: <TypeExpression> <VariableName>-opt <Ellipsis>

<ReturnArgument>: -> <FormalArgument>

<VariableName>: <UpperCaseIdentifier>

Predicate domains can have <Ellipsis> argument as the last <FormalArgument> in the <FormalArgument>-comma-sep-list.

Predicate domains can have <AnonymousIdentifier> as a to specify that the argument can be of any type.

Currently, predicate domains with ellipsis can only be used in predicate declarations.

Predicate domains that are used in domain definitions can at most state one flow.

<PredicateModeAndFlow>: <PredicateMode>-opt <FlowPattern>-list-opt

Predicate Mode
The specified predicate mode applies for each member of a flow pattern list following it.

<PredicateMode>: one of  erroneous failure procedure determ multi nondeterm

Predicate modes can be described by the following sets:

erroneous = {} failure = {Fail} procedure = {Succeed} determ = {Fail, Succeed} multi = {Succeed, BacktrackPoint} nondeterm = {Fail, Succeed, BacktrackPoint}

If Fail is in the set it means that the predicate can fail. If succeed is in the set it means that the predicate can succeed. If BacktrackPoint is in the set it means that the predicate can return with an active backtrack point in it.

If such a set, say <vp>failure</vp>, is a subset of another set, say <vp>nondeterm</vp>, then we say that the mode is stronger than the other, i.e. <vp>failure</vp> is stronger than <vp>nondeterm</vp>.

A predicate domain actually contain all predicates (with correct type and flow), which have the mode specified or a stronger mode.

It is illegal to state a predicate mode for constructors, they always have the <vp>procedure</vp> mode.

Omitting of a predicate mode means <vp>procedure</vp>.

Flow Pattern
The flow pattern defines the input/output direction of the arguments, which in combination with functor domains can be structures with parts of a single argument being input and other parts of the same argument being output.

A flow pattern consists of a sequence of flows, each flow corresponds to an argument (fist flow to first argument, etc).

<FlowPattern>: ( <Flow>-comma-sep-list-opt ) AnyFlow

<Flow>: one of   i    o     <FunctorFlow> <ListFlow> <Ellipsis>

Ellipsis flow must match an ellipsis argument and can therefore be only the last flow in the flow pattern.

<Ellipsis>: ...

A functor flow <FunctorFlow> states a functor and flows of each of the components of that flow. The functor must of course be in the domain of the corresponding argument.

<FunctorFlow>: <FunctorName> ( <Flow>-comma-sep-list-opt )

A functor flow declaration cannot contain ellipsis flow.

List flows are just like functor flows, but with the same syntactic sugaring as the list domain.

<ListFlow>: [ <Flow>-comma-sep-list-opt <ListFlowTail>-opt]

<ListFlowTail>: | <Flow>

A list flow cannot contain ellipsis flow.

When declaring a predicate the flow can be omitted. Inside an implementation (i.e. for a local predicate) the needed flows are derived from the usages of the predicate. Inside an interface or a class declaration (i.e. for a public predicate) omitting flows means that all arguments are input.

The special flow pattern <vp>anyflow</vp> can be stated only in declarations of local predicates (i.e. in predicate declarations inside the implementation of a class). It means that the exact flow pattern(s) will be evaluated during the compilation.

Calling Convention
The calling convention determines how arguments, etc. are passed to the predicate, it also determines how the link name is derived from a predicate name.

<CallingConvention>: language <CallingConventionKind>

<CallingConventionKind>: one of    c thiscall stdcall apicall prolog

If a calling convention is not stated, then the <vp>prolog</vp> convention is assumed. The <vp>prolog</vp> calling convention is the standard convention used for Prolog predicates.

The calling convention <vp>c</vp> follows the C/C++ standard calling convention. The link name of a predicate is created from the predicate name by adding a leading underscore (<vp>_</vp>).

The calling convention <vp>thiscall</vp> follows the C++ standard calling convention for virtual functions. This calling convention uses the <vp>c</vp> link name strategy but sometimes it may use the different argument and stack handling rules. Calling convention <vp>thiscall</vp> can be applied to the object predicates only.

The calling convention <vp>stdcall</vp> uses the <vp>c</vp> link name strategy but it uses the different argument and stack handling rules. The following table shows the implementation of <vp>stdcall</vp> calling convention:

The calling convention <vp>apicall</vp> uses the same argument and stack handling rules as <vp>stdcall</vp>, but for convenience to call MS Windows API functions <vp>apicall</vp> uses the naming conventions that are used by most MS Windows API functions. According to <vp>apicall</vp> naming conventions the link name of a predicate is constructed as follows:


 * an leading underscore (<vp>_</vp>) is prefixed to the predicate name;
 * the predicate name in which the first letter is changed in to a capital letter;
 * the '<vp>A</vp>', the '<vp>W</vp>' or nothing is suffixed, if the arguments and the return type indicate an ANSI, Unicode or neutral predicate, respectively;
 * the '<vp>@</vp>' is suffixed;
 * the number of bytes pushed on the call stack is suffixed.

If <vp>apicall</vp> is used together with the "<vp>as</vp>" construction the name stated in the "<vp>as</vp>" construction is decorated in the same manner.

<vp>apicall</vp> can only be used directly in a predicate declaration, not in a predicate domain definition. In predicate domain definitions <vp>stdcall</vp>, must be used instead. A predicate declared with <vp>apicall</vp> calling convention cannot have clauses and it also cannot be resolved externally without explicit DLL name.

The following table compares implementations of <vp>c</vp>, <vp>apicall</vp>, and <vp>stdcall</vp> calling conventions (the <vp>prolog</vp> calling convention has the special implementation, which is not discussed here):

Visual Prolog notion of predicate domains covers both class and object members. Class members are handled straight forward, but the handling of object members requires attention. The invocation of an object predicate will get "back" in the context of the object to which the member belongs.

Format Strings
A formal parameter to a predicate can be marked as format string using the attribute. The format string can contain ordinary characters which are printed without modification, and format fields, that % begins with the percent '%' sign. If the percent sign is followed % by some unknown character (not the format specifier) - then % this character will be printed without modifications.

predicates writef : (string Format [formatstring], ...).

The format fields specification is:

[-][0][width][.precision][type]

All fields are optional.

[-] Hyphen indicates that the field is to be left justified; right justified is the default. Having no effect when width value is not set, or the number of characters in the actual value is greater than width value.

[0] Zero before width means for values that zeros will be added until the minimum width is reached. If 0(zero) and -(hyphen) appear, the 0 is ignored

[width] Positive decimal number specifying a minimum field size. If the number of characters in the actual value is less than width value - then the required number of space ' ' characters will be added before the value (or after it, if '-' field was set). No changes occurs if number of characters in the actual value is greater than the width value.

[.precision] The point '.' with the following unsigned decimal number can specify either the precision of a floating-point image or the maximum number of characters to be printed from a string.

[type] Specifies other format then the default for the given. For example, in the type field, you can give a specifier that says an integer will be formatted as an unsigned. The possible values are:




 * f || Format real's in fixed-decimal notation (such as 123.4 or 0.004321). This is the default for real's.
 * e || Format real's in exponential notation (such as 1.234e+002 or 4.321e-003).
 * g || Format real's in the shortest of f and e format, but always in e format if exponent of the value is less than -4 or greater than or equal to the precision. Trailing zeros are truncated.
 * d or D || Format as a signed decimal number.
 * u or U || Format as an unsigned integer.
 * x or X || Format as a hexadecimal number.
 * o or O || Format as an octal number.
 * c || Format as a char.
 * B || Format as the Visual Prolog binary type.
 * R || Format as a database reference number.
 * p || Format as the presented value.
 * P || Format as a procedure parameter.
 * s || Format as a string.
 * }
 * c || Format as a char.
 * B || Format as the Visual Prolog binary type.
 * R || Format as a database reference number.
 * p || Format as the presented value.
 * P || Format as a procedure parameter.
 * s || Format as a string.
 * }
 * p || Format as the presented value.
 * P || Format as a procedure parameter.
 * s || Format as a string.
 * }
 * s || Format as a string.
 * }

Integral Domains
Integral domains are used for representing integral numbers. They are divided in two main categories for signed and unsigned numbers. Integral domains can also have different representation size. The predefined domains and  represent signed and unsigned numbers with natural representation length for the processor architecture (i.e. 32bit on a 32bit machine, etc).

<IntegralDomain>: <DomainName>-opt <IntegralDomainProperties>

If a <DomainName> is stated in front of the <IntegralDomainProperties>, then this domain must itself be an integral domain and the resulting domain will be child-type (i.e. subtype) of this domain. In that case <IntegralDomainProperties> may not violate the possibility of being a subtype, i.e. the range cannot be extended and the size cannot be changed.

<IntegralDomainProperties>: <IntegralSizeDescription> <IntegralRangeDescription>-opt <IntegralRangeDescription> <IntegralSizeDescription>-opt

<IntegralSizeDescription>: bitsize <DomainSize>

<DomainSize>: <IntegralConstantExpression>

An integral size description declares the size <DomainSize> of the integral domain, measured in bits. The compiler implement such representation to the integral domain, which has no less than the specified number of bits. The value of <DomainSize> should be positive and no greater than the maximal value supported by the compiler.

If integral size description is omitted, then it will become the same as the parent domain. If there is no parent domain, it will become the natural size for the processor.

<IntegralRangeDescription>: [ <MinimalBoundary>-opt .. <MaximalBoundary>-opt ]

<MinimalBoundary>: <IntegralConstantExpression>

<MaximalBoundary>: <IntegralConstantExpression>

An integral range description declares the minimal <MinimalBoundary> and the maximal <MaximalBoundary> limits for the integral domain. If a limit is omitted, then the range of the parent domain is used. If there is no parent domain, then the <DomainSize> is used to determine respectively maximum or minimum value.

Notice that the specified minimum value should not exceed the specified maximum value. That is:

<MinimalBoundary> <= <MaximalBoundary>

Also the minimal <MinimalBoundary> and the maximal <MaximalBoundary> limits should satisfy the limits implied by the specified bit size <vp>bitsize</vp>.

The domain bit size <DomainSize> value and values of the minimal <MinimalBoundary> and the maximal <MaximalBoundary> limits must be calculated while compiling time.

Real Domains
Real domains are used to represent numbers with fractional parts (i.e. floating point numbers). Real domains can be used to represent very large and very small numbers. The built-in domain real have the natural precision for the processor architecture (or the precision given by the compiler).

<RealDomain>: <DomainName>-opt <RealDomainProperties>

If a <DomainName> is stated in front of the <RealDomainProperties>, then this domain must itself be a real domain and the resulting domain will be a subtype of this domain. In that case <RealDomainProperties> may not violate the possibility of being a subtype, i.e. the range cannot be extended and the precision cannot be increased.

<RealDomainProperties>: one of   <RealPrecisionDescription> <RealRangeDescription>-opt <RealRangeDescription> <RealPrecisionDescription>

<RealPrecisionDescription>: digits <IntegralConstantExpression>

The real precision description declares precision of the real domain, measured in number of decimal digits. If precision is omitted then it will become the same as for the parent domain. If there is no parent domain, then it will be the natural precision for the processor or given by the compiler (in Visual Prolog v.6 the compiler limit is 15 digits). Precision have an upper and a lower limits given by the compiler, if the precisions larger than that limit is used the numbers will only obtain the processor (compiler) specified precision anyway.

<RealRangeDescription>: [ <MinimalRealBoundary>-opt .. <MaximalRealBoundary>-opt ]

<MinimalRealBoundary>: <RealConstantExpression>

<MaximalRealBoundary>: <RealConstantExpression>

Here <RealConstantExpression> is an expression, which must be compile time evaluated to a floating point value. That is the real domain precision and limits must be calculated while compiling time.

The real range description declares minimal and maximal limits for the real domain. If a limit is omitted then it will be the same as for the parent domain. If there is no parent domain then the largest possible range for the precision will be used.

Notice that the specified minimum value should not exceed the specified maximum value. That is:

<MinimalBoundary> <= <MaximalBoundary>

Generic Domains
This section contains the formal syntax for generic domains, for a more complete introduction to generics please see the tutorial Objects and Polymorphism and the section Generic Interfaces and Classes.

<FormalTypeParameterList>: <TypeVariable>-comma-sep-list-opt

A formalTypeParameterList is a list of typeVariables

<TypeVariable>: <UpperCaseIdentifier>

A <TypeVariable> is an upper case identifier. In a domain declaration the type variable must be bound in the <FormalTypeParameterList> on the left hand side of the domain definition. In a predicate declaration all free type variables are implicitly bound and scoped to that predicate declaration.

<TypeApplication>: <TypeName> {<TypeExpression>-comma-sep-list-opt }

A <TypeApplication> is the application of a to a list of types. The type name must be generic and the number of formal type parameters must match the number of type expressions.

Universal and Root Types
Visual Prolog uses some internal types, called root types and universal types.

Universal Types
A number literal like <vp>1</vp> does not have any particular type, it can be used as a value of any type that contains <vp>1</vp>, including real types.

We say that <vp>1</vp> have a universal type. Having a universal type means that it have any type, which can represent its value.

Arithmetic operations also return universal types.

Root Types
Arithmetic operations are very liberal with their operand requirements: You can add integers of any integer domain with each other.

We say that arithmetic operands takes root types as arguments. The integer root type is super-type of any integer type (regardless that it is not mentioned in their declarations). Hence any integer type can be converted to the integer root type, and, since the arithmetic operations exist for the root types, it means one of them will work on any integer domains.

The actual number of root types and which operands exist is a matter of library facilities, and outside the scope of this document to describe.