1.2. AML-DL Interpreter Implementation¶

A viable implementation consists of a few commands implemented as functions of a base language like Python or C++. Using a base language has obvious advantages as the entire base language is available to add control loops, variables, etc. Relying on a base language makes the implementation simpler as it makes unnecessary to use a parser or a lexer.

A single descriptor object can be used for the commands’ input and output. AML-DL commands correspond to functions of the base language that take one or many descriptor objects and return a descriptor object. It is enough with a few types of descriptor objects: type CONST, type SET, type VECTOR, type CVECTOR, type TVECTOR, and type DUPLE.

Descriptors of type CONST correspond to constants defined using the command \(C\) while descriptors of type CVECTOR correspond to vectors of constants returned by the command \(CV\).

The return values of commands \(C\), \(CV\) , \(INC\), and \(EXC\) are final descriptors, in the sense that they are appended to the program output. The program output is comprised of a set of descriptors of type CONST and a set of descriptors of type DUPLE. Other commands, like \(M\) , \(S\), \(T\) , \(V\) , or \(R\) return descriptors that are not appended to the program output and that are meant to be used as inputs of other commands or assigned to a variable. In other words, these commands return descriptors that should be bound. Hence, descriptors are either final or bound. The binding of a descriptor to an input parameter of a command may occur directly or by means of a variable of the base language.

At any point, the program state corresponds to a set of final descriptors collected so far, and a dictionary that maps names to descriptor objects. Names are regular character string that can be used to invoke descriptor objects by name. The find command F is provided to specifically retrieve a descriptor by name although, for convenience, names can always be used instead of descriptor objects as parameters of any command.

The command \(V([name])\), which declares a vector, produces a descriptor that can be optionally bound. As a side effect, and only if a name is provided, a newly created empty vector is associated with said name. It returns a descriptor of type VECTOR.

Descriptors of type SET are returned by the commands \(M\) and \(S\). A SET descriptor corresponds to a set of constants. When a VECTOR or CVECTOR is provided as an input to \(M\) or \(S\) their components are added to the returned set. The \(M\) command, which corresponds to the idempotent operator, adds every provided parameter to the returned set. These sets are going to be interpreted as the component constants of terms, so they will become elements of the semilattice.

Descriptors of type DUPLE correspond to duples and can be positive when produced by the \(INC\) command, or negative when produced by the \(EXC\) command. Type DUPLE descriptors are always final. The \(INC\) and \(EXC\) commands require inputs of type SET.

The iterator command \(T\) provides indexing capabilities and makes the language significantly more flexible. It can operate with any other command. The iterator command changes descriptors of type SET, VECTOR or CVECTOR into descriptors of type TVECTOR. When a command has an input of type TVECTOR, the command is applied not to the TVECTOR itself but to each one of its components. The function then returns a TVECTOR whose components are the corresponding returned values.

A command may receive one or many inputs of type TVECTOR. Descriptors of type TVECTOR have an index number. If a command receives more than one input of type TVECTOR with the same index the components are replaced at the same time in all of them. In this case, all the TVECTOR descriptors with the same index should have the same dimension otherwise an error is returned. For example, a command that receives one or many descriptors of type TVECTOR of size n and the same index returns a TVECTOR of size \(n\). When a command receives more than one input of type TVECTOR with different indexes the output corresponds to a TVECTOR with components of type TVECTOR producing a tree of depth equal to the number of different indexes used. For example, if a command receives two descriptors of type TVECTOR with sizes \(m\) and \(n\) then it produces a TVECTOR of size m whose components are descriptors of type TVECTOR of size n, therefore producing a total of \(m \times n\) applications of the command.

Descriptors form a tree. We have just mentioned the case of nested descriptors of type TVECTOR. Descriptors of type DUPLE consist of a pair of references to descriptors of type SET, one to the left and the other to the right-hand descriptors. Descriptors of type VECTOR have components of any type except DUPLE.