Breeze XSBL
eXtensible Stack Based Language
Version 0.1.1
USER MANUAL
This
is alpha software, which means it is usable and the feature list is almost
complete but not totally implemented.
Disclaimer
The information provided in this document is
provided "AS IS" without warranty of any kind, either express or
implied, including, but not limited to, the warranty of non-infringement and
the implied warranties of merchantability and fitness for a particular purpose.
Verbatim, copying and redistribution are permitted in all media for any
purpose, provided all copyright notices are preserved along with related
original URLs.
Copyright © 2015 Mauro DiNuzzo
2.2.1 The basics: numbers and lists
2.2.2 The special application #
2.2.3 Including external files
3.1.1 Named stacks manipulation
3.1.2 Interpreter and
meta-programming
3.1.3 Standard stack manipulation
3.2.3 Operating System Interactions
The
present project aims at providing a pure-homoiconic, pure-postfix stack-based programming
language. At this stage, it is only a proof-of-concept, whose details might
change substantially in future releases. For the sake of these preliminary
testing procedures, the language is being developed on top of the SWI Prolog (http://www.swi-prolog.org/) environment.
Purity and minimalism, not computational speed, have been the driving forces of
the software. Nonetheless, possible success of this stage could stimulate developing
the language in C/C++. User should use this software ONLY for learning/testing
purposes.
At the
moment, the only supported operating system is Windows. There are no plans to
compile the software under other platforms/operating systems. Users interested
in running the software on Linux or MacOS should contact the author directly
via email at info@prologwebservices.com.
What
follows is a brief tutorial on how to download, install and use the Breeze programming
language. For more details about concatenative languages, see http://concatenative.org/.
The
archive containing the software and documentation is available through
SourceForge at http://sourceforge.net/projects/breeze-language/. To install the software, simply run the self-installer
executable. At startup, the interpreter loads the init.brz initialization file, which is expected to be in the main program directory.
Normally, this file is used to include standard libraries, but can be edited to
incorporate user-specific operations.
SWI Prolog
(http://www.swi-prolog.org/Download.html) is required in order to compile the sources. To this end, consult
the make.pl Prolog script located
in the src directory.
The Breeze.exe
executable will be automatically created in the bin directory. The executable file does not yet support command
arguments. Version 0.1.1 is compiled using SWI Prolog 6.6 (32 bit).
Presently,
the language inherits the limit stack size from the hosting programming
language, which is of 128MB for the x86/win32 version of SWI Prolog.
Please also note that the description of the language given in the
following chapters is far from exhaustive. In particular, many features of the
language are not discussed throughout the manual. Nonetheless, chapter 3
contains the list of all applications (even those poorly or not documented).
The language is being enriched with libraries (e.g., regular
expressions, ODBC support, HTTP client/server) that will be included in the
distribution as they become usable (i.e. at least with a minimum of useful
functionality).
Testing procedure are currently not operational and there is not
yet a testing suite available for the language.
Breeze is
a general-purpose programming language. The list of basic features can be
summarized as follows. The language is:
The Breeze
language shares many similarities with other popular stack-oriented programming
languages. The main influences can be found in Chuck Moore’s Forth (http://www.forth.org/), Kevin Albrecht’s
Joy (http://www.kevinalbrecht.com/code/joy-mirror/), Christopher Diggins’s Cat (http://www.cat-language.com/) and Slava Pestov’s Factor (http://www.factorcode.org/). The notion of a purely postfix-notation language also comes
from Riad S. Wahby's Lviv (http://github.com/kwantam/lviv). It should be realized, however, that the sparse ideas taken
from these languages have been “filtered” towards simplicity. The most striking
difference from the above-mentioned concatenative languages, which is also the
main feature of the language, is that the concepts of stack and dictionary are
no longer distinct. In fact, dictionary words are replaced by named stacks. The
process of defining a word is replaced by “pushing” onto a named stack, and
that of executing a word is replaced by “popping” from a named stack. A named
stack is called an application
(strictly speaking, the application is represented by the code/data within the
top element of the relevant named stack). The pushing and popping operations
share an anonymous stack, where the actual applications work by default. Specifically,
data and code can be moved between applications by using operations (i.e. other
applications) that make use of a default stack as an intermediate. This
approach is described in some details throughout the present manual.
In principle,
the language recognizes only numbers (integers and floats, no core support for
rational or complex numbers). Numbers can be organized into lists, which are
enclosed in round parenthesis and can be arbitrarily complex structures
containing zero or more elements (numbers or lists). Lists are the only form of
aggregation of data/code. A distinguishing feature of the language is that a list
might or might not be interpreted, and interpretation of a list must be explicitly
required. Notably, no control is performed on list content. Indeed, the system
cannot know whether a list is supposed to contain data or code. This feature
gives the user the complete control on the program. The same representation for
code and data (i.e. homoiconicity) identifies the powerful meta-programming
capability of the language. From the above arguments, it follows that a program
is actually a list containing numbers as well as lists of numbers. The latter
feature makes the language purely untyped and also entails that run-time interpretation
is the only possible execution mode.
In the practice,
any sequence of characters that is not evaluable as a number or as a list is
interpreted as a short-hand for a set of operations that could however be
achieved by using only numbers and the special application # (see below). Note that the
following characters are reserved:
·
␣ (whitespace, used to separate elements);
·
( ) (open/closed parenthesis, used to
aggregate data/code);
·
; (semicolon, used for inline comments);
The escape
sequences \s and, for
example, \50, \51 and \73 (i.e. octal character
representation) can be used to represent whitespace, round parentheses and
semicolon, respectively. Note that the number of spaces is never preserved,
which means that spaces must be provided as escape sequence when they are
needed (e.g., to print out things on the screen).
Breeze is a run-time interpreted language. Presently, compilation
is not implemented and the only interface to the language is a window
containing a prompt waiting for user input.
This section briefly illustrates the main features of the language
by giving some simple examples. Hereafter, the red color is used to identify
code/data segments typed by the user, green color is used for comments and gray
color is used for system responses. Stack names (i.e. applications) will be
always introduced in the upper-case mode. However, the language is not case-sensitive.
Unless otherwise specified, the default stack is referred to simply as the
stack.
Since Breeze is a stack-based programming language, data follow the
last-in first-out (LIFO) mechanism. Data received on the input stream
(initially the standard input) are pushed on the default
stack. For example, pushing the integer 100
and the floats 2.0 and 3.0
on the stack can be done as follows:
100 2.0 3.0
In this case the numbers are separated by one or more space
characters. Space is the standard “operator” used as separator.
In order to see the content of the stack the application
PS can be used, as in:
ps ; Breeze is not case-sensitive, thus “ps” behaves like
“PS” 100 2.0 3.0 DROP PS ; DROP removes the last element on top of the stack
100 2.0
Applications can be used to perform operations on data. For
example, the sum of two numbers on top of the stack can be accomplished by +:
100 2.0 + PS 102.0
The two inputs have been “popped” (i.e. removed) from the stack,
then the operation has been performed and finally the result of the computation
has been “pushed” (i.e. added) onto the stack. Note that the result is float
because one of the two numbers of the sum is float. The + application
(i.e. the code contained in the + named stack) expects to find two
numbers on top of the default stack. If this is not the case, an evaluation
error (terms are not numbers) or a stack underflow error (stack does not
contain enough terms) will be raised (see the THROW application in Section 3).
Let’s now assume that one wants to implement the “successor” operation
in the SUCC stack. This can be done as follows:
(1 +) (SUCC) PUSH
In the example, PUSH pushes the list (1 +) onto
the SUCC application (note that the SUCC stack is created on the fly if it does
not exist yet). The parentheses define a list, which is not interpreted but is
simply pushed on the default stack.
Now it is possible to use the SUCC application to perform the successor
operation, that is:
102.0 SUCC PS 103.0 ( ) CLR ; ( ) identify the default stack
The application CLR used above, clears (i.e. remove all
elements from) an entire stack, in this case the default stack (identified by
the empty list preceding CLR). Using the SUCC application
by typing the name directly is equivalent to the following sequence of
operations:
102.0 (SUCC) TOP PS ; 1. TOP puts the top element in SUCC onto the default
stack (1 +) APPLY ; 2. APPLY puts the list into use PS ( ) CLR 103.0
The APPLY application “unwrap” the list on top of the stack, which is
equivalent to execute the data/code contained in the list. An important feature
of the language is that, by definition, it is straightforward to override an
application (without losing anything previously pushed on the named stack
itself), as in:
(2 +) (SUCC) PUSH (SUCC) TOP PS DROP ; just to see what is on top of the SUCC stack now (2 +) 10 SUCC PS DROP ; this
applies the new definition 12
The operation SEE can be used to see the entire content
of a stack, that is:
(SUCC) SEE PS DROP ((1 +) (2 +))
Elements contained in a stack can be removed using POP or CLR. For
example, the second definition of SUCC given above can be removed by:
(SUCC) POP DROP 10 SUCC PS DROP ;
using the old definition, as it is now on top 11
Importantly, as stated before the code in the named stack
identified by SUCC
that is interpreted by just typing the corresponding name, is a short-hand for
a set of operations that can be obtained using the following code:
100 (83 85 67 67) # (80 83) # 101
The special application # is the sole mandatory symbolic element
known by the system (i.e. to avoid any circular definition). The rationale
underlying the use of #
is simple: it accepts a list of numbers identifying the Latin-1 (ISO 8859-1
encoding) character codes of a name. This list might be also well interpreted simply
as an arbitrary code identifying that name. Anyway, in the above example 83 85
67 67 is the Latin-1
code for SUCC
and 80 83 is the Latin-1 code for PS. Note that the Latin-1 code is unique
and it refers to the upper-case mode of a name.
Oddly, writing programs this way would be very inefficient.
Nonetheless, the # stack serves to illustrate the point that numbers and names can be
considered analogous entities. For practical purposes, the # application is not a primitive, but it
is defined as the sequence (CHR APPLY). The CHR operation converts a list of Latin-1-encoded characters into its
representation, while APPLY puts into use the content of the list
on top of the default stack.
The use of APPLY can be also illustrated by the
following example:
(1 2.0 +) PS (1 2.0 +) APPLY PS 3.0
Note that initially the stack contains one element: the whole list,
which in turn contains three elements. The APPLY application is equivalent to interpret
the content of the list. The inverse operation of APPLY is performed by POP, as in
the following example:
3.0 ( ) POP PS DROP (3.0)
The POP operation performed on the default stack is thus useful as list
constructor, as it “wraps” the last element on top of the stack.
The following example uses the INCLUDE application to switch the current
input to a physical file:
(examples/fact.brz) INCLUDE 5 FACT1 PS ; “FACT1” is defined in “examples/fact.brz” 120
Note that a file might in turn include other files. To avoid
multiple inclusion, the INCLUDED operation can be used to check whether
a file has already been loaded, which is what the ?INCLUDE application actually does.
This section describes how to add and remove terms to/from named stacks.
When the software starts, only primitives are defined. One of the design
feature of the language is to keep the number of these primitives as low as
possible. Thus, normally at startup several files are included that contain a
number of definitions.
The standard actions to manage a named stack are achieved by means
of the primitives PUSH, POP and TOP. A simple example is the storage of a constant numeric value:
(include/math.brz) ?INCLUDE (pi) (x) PUSH ; note that “pi” is defined in “include/math.brz” x 2 * PS DROP 6.2831852 (x) POP x Uncaught Exception
-- STDIN:5: Stack Error (UNDERFLOW X)
The POP application removes the top element of a named stack and puts it
on the default stack. It is possible to override even primitives by pushing
arbitrary data/code onto them. The system stores the original definition in
reserved stacks (see Section 3). Addition/removal of data or code to/from
reserved stacks is not allowed.
The standard control mechanism of the language is the simple If-Then-Else
construct, which is identified by the primitive IFTE. It can be shown that this mechanism
plus recursion is sufficient to achieve all control flow statements such as
while/repeat loops, switch/case structures, and so on. As anything in Breeze,
the If-Then-Else construct is implemented in a purely postfix notation, as in:
1 2.0 (DUP 0 <) (-) (+) 1 2.0 (DUP 0 <) (-) (+) IFTE PS 3.0
The IFTE operation expect to find three lists on the top of the default stack
(the choice of the lists order is motivated by program legibility). In
particular, the user writes before the If condition, and subsequently the Then
and Else parts. Within the If part, the above example makes use of the DUP operation, which duplicates the top
element of the stack. After this, 0 is pushed and < (less-than) comparison is made, which
removes two numbers from the stack and pushes the Boolean result (in this case 0, i.e. false). A Boolean value is what
the operation IFTE
expect to be contained in the If list. The other two lists are self-explaining:
when the Boolean values is true the True part is applied and the Else part is
“dropped”, and vice versa when the Boolean values is false. Note that numbers
greater than zero are interpreted as Boolean true, whilst numbers less than or
equal to zero are interpreted as Boolean false.
The If-Then-Else construct is the only primitive for control flow.
As an illustration, user can examine the While-Loop implementation using IFTE and LAMBDA recursion described in Chapter 3. As
an example of recursion, the following code implements the factorial:
( ; n (DUP 0 =) ; n n 0 = (DROP 1) ; 1 ( ; n DUP 1 - ; n n-1 FACT ; recursion * ; n n-1
n-2 ... 1 * * * … * (n-1 times) ) IFTE ) (FACT) PUSH ; FACT will compute
the factorial of the number on top of the stack 5 FACT PS 120
The above is the standard definition of the factorial using
recursion. Another (computationally slower) version using only list
manipulation can be found in the examples directory.
A very simple version of Lambda calculus is implemented using local
definitions (for the definition of LAMBDA see Chapter 3). For example, consider
the simple successor operation of a number as defined above. It is possible to
obtain the same result without explicitly defining a new stack using LAMBDA:
2.0 (1 +) LAMBDA PS 3.0
This behavior might appear to be identical to that obtained using
the APPLY
operation, and this is indeed the case in the above example. However, LAMBDA comes to be very useful when recursion
is required as in the factorial example:
5 ((DUP 0 =) (DROP 1) (DUP 1 - REC *) IFTE) ; same body of FACT1 (see above) LAMBDA PS 120
Recursion is achieved using the REC operation, which is defined only
within a LAMBDA operation. Please note that LAMBDA and REC are not part of the core primitives. Note
also that LAMBDA
can be nested, as subsequent definitions are pushed in the REC stack. Lambda
programming is often used when an operation is expected to return more than one
result and/or to perform many sub-computations to store temporal results
without defining new stacks (see the SEL and INS operations described in Chapter 3).
In this
chapter, the built-in named stacks implemented in the language are described in
some details. Stacks corresponding to core operations are marked as “primitive”,
whereas the other applications are defined in terms of these primitives.
This section briefly describes the stacks that exist at startup
without loading any external library (note that the default init.brz
startup file include several files). The description is accompanied by the
effect of the operation on the default stack. Specifically, the local stack
prior to the call is shown on the left, while the local stack after the call is
shown on the right (separated by a rightward arrow).
The stacks are
accessed and manipulated using the primitives PUSH, POP, TOP, DEPTH, SEE and CLR.
PUSH |
PRIMITIVE |
( A1…An
) ( B ) →
Push A1…An in the stack
referenced by B (i.e. the stack
name representation).
POP |
PRIMITIVE |
( B ) → ( A1…An )
Remove the top element of the stack B and move it to the default stack.
TOP |
PRIMITIVE |
( B ) → ( A1…An )
Returns the top element of the stack B (without removing it) and push it to the default stack.
DEPTH |
PRIMITIVE |
( B ) → C
Returns the number of elements in the stack B.
SEE |
PRIMITIVE |
( B ) → ( ( A1…An ) … ( Z1…Zn ) )
Push the entire content of stack B on top of the default stack (as a single list) non-destructively
(i.e. without removing anything).
CLR |
PRIMITIVE |
( B ) →
Remove all
elements from the stack B.
System primitives include the EXIT, ABORT, INCLUDE and INCLUDED operation. Meta-programming is realized through the primitive APPLY and the non-primitive LAMBDA and REC operations. The non-primitive NOP operation is also described here.
EXIT |
PRIMITIVE |
Exit (halt system)
ABORT |
PRIMITIVE |
Abort execution. This is equivalent to press CTRL+C. Execution is
also aborted after uncaught exceptions.
INCLUDE |
PRIMITIVE |
( A ) →
Tells the interpreter to receive
input from an external resource A (presently, only source files are accepted).
INCLUDED |
PRIMITIVE |
( A ) → 0 | 1
Return a
Boolean value according to whether the resource A has been already loaded.
?INCLUDE |
|
( A ) →
Include the
external resource A only if it has
not been already loaded. This operation is defined as:
((DUP INCLUDED) (DROP) (INCLUDE) IFTE) (?INCLUDE) PUSH
APPLY |
PRIMITIVE |
( An…A0
) → An…A0
Put the
list An…A0 into use.
# |
|
( A1…An
) →
Interpret the content of the stack identified by its ISO Latin-1
code, as returned by the list of numbers A1…An. This operation is defined as:
(CHR APPLY) (#) PUSH
LAMBDA / REC |
|
( A1…An ) →
Locally apply the program A1…An, and define the helper operation REC that can be used to achieve recursion. Note that on error REC will still be defined and its removal, if needed, must eventually
be done using CATCH. This operation
is not a primitive and it is defined as:
((REC) DEF REC (REC) POP DROP) (LAMBDA) PUSH
NOP |
|
No operation. NOP can be used to
explicitly state that no action is required. This operation is defined as:
( ) (NOP) PUSH
Default stack manipulation is accomplished by the primitives DROP, PICK, and ROLL. By using these primitives it is
possible to perform complex operation on the elements of the stack, as will be
described in the following.
DROP |
|
PRIMITIVE |
A →
Remove the top element from the stack.
PICK |
PRIMITIVE |
A0…An n → A0…An A0
Duplicate nth stack element on top of the stack, where the top of
the stack position is identified by index 0.
ROLL |
PRIMITIVE |
A0…An n → A1…An A0
Move nth element of the stack on top of the stack, where the last
element pushed on top of the stack is identified by index 0.
DUP |
|
X → X X
Duplicate the top element of the stack. This operation is defined
as:
(0 PICK) (DUP) PUSH
?DUP |
|
X → 0 | X X
Duplicate the top of the stack if it is non-zero. This operation
is defined as:
((DUP 0 =) (NOP) (DUP) IFTE) (?DUP) PUSH
OVER |
|
X Y → X Y X
Duplicate the second to last element of the stack. This operation
is defined as:
(1 PICK) (OVER) PUSH
SWAP |
|
X Y → Y X
Exchange the last two elements on the stack. This operation is
defined as:
(1 ROLL) (SWAP) PUSH
ROT |
|
X Y Z → Y Z X
Move the third to last element of the stack on top. This operation
is defined as:
(2 ROLL) (ROT) PUSH
-ROT |
|
X Y Z → Z X Y
Move the last element of the stack as third to last (inverse of ROT). This operation is defined as:
(ROT ROT) (-ROT) PUSH
NIP |
|
X Y → Y
Remove the second to last element of the stack. This operation is
defined as:
(SWAP DROP) (NIP) PUSH
TUCK |
|
X Y → Y X Y
Duplicate the top element of the stack as third to last. This operation
is defined as:
(SWAP OVER) (TUCK) PUSH
2DUP |
|
X1 X2 → X1 X2 X1 X2
Duplicate pair. This operation is defined as:
(OVER OVER) (2DUP) PUSH
2DROP |
|
X1 X2 →
Remove pair. This operation is defined as:
(DROP DROP) (2DROP) PUSH
2SWAP |
|
X1 X2 Y1
Y2 → Y1 Y2 X1 X2
Swap pair. This operation is defined as:
(3 ROLL 3 ROLL) (2SWAP) PUSH
2OVER |
|
X1 X2 Y1
Y2 → X1 X2 Y1 Y2 X1 X2
Duplicate second to last pair. This operation is defined as:
(3 PICK 3 PICK) (2OVER) PUSH
2NIP |
|
X1 X2 Y1
Y2 → Y1 Y2
Remove the second to last pair. This operation is defined as:
(2SWAP 2DROP) (2NIP) PUSH
2TUCK |
|
X1 X2 Y1
Y2 → Y1 Y2 X1 X2 Y1 Y2
Tuck pair in third to last. This operation is defined as:
(2SWAP 2OVER) (2TUCK) PUSH
2ROT |
|
X1 X2 Y1
Y2 Z1 Z2 → Y1 Y2 Z1 Z2 X1 X2
Move third to last pair on top. This operation is defined as:
(5 ROLL 5 ROLL) (2ROT) PUSH
-2ROT |
|
X1 X2 Y1
Y2 Z1 Z2 → Z1 Z2 X1 X2 Y1 Y2
Inverse of 2ROT. This operation
is defined as:
(2ROT 2ROT) (-2ROT) PUSH
The following four primitives allows for creation and handling of
lists. In particular, there are the constructors CONSL and CONSR, and the destructors UNCONSL and UNCONSR.
CONSR |
PRIMITIVE |
( An…A1
) ( A0 ) → ( An…A0 )
List constructor. Insert unit A0 on the right of the list An…A1.
CONSL |
PRIMITIVE |
( A1…An
) ( A0 ) → ( A0…An )
List constructor. Insert unit A0 on the left of the list A1…An.
UNCONSR |
PRIMITIVE |
( An…A0
) → ( An…A1 ) ( A0 )
List destructor. Remove unit A0 from the right of the list An…A0.
UNCONSL |
PRIMITIVE |
( A0…An
) → ( A1…An ) ( A0 )
List destructor. Remove unit A0 from the left of the list A0..An.
FOLDR |
|
( An…A1 ) B ( C1…Cm ) → D
Right fold the list A1…An starting from seed B using the operations contained in the list C1…Cm, and push the result D on the stack. This operation is defined as:
( DUP 2SWAP SWAP (DUP NULL =) (2NIP DROP) (UNCONSR APPLY 2SWAP -ROT APPLY ROT FOLDR) IFTE ) (FOLDR) PUSH
FOLDL |
|
( A1…An ) B ( C1…Cm) → D
Left fold the list A1…An starting from seed B using the operations contained in the list C1…Cm, and push the result D on the stack. This operation is defined as:
( DUP 2SWAP SWAP (DUP NULL =) (2NIP DROP) (UNCONSL APPLY 2SWAP -ROT APPLY ROT FOLDL) IFTE ) (FOLDL) PUSH
UNFOLDR |
|
( An…A1 ) B ( C1…Cm) D → ( E1…El )
Right unfold the list A1…An starting from seed B using the operations contained in the list C1…Cm until condition D is satisfied, and push the result list E1…El on the stack. This operation is defined as:
( 2SWAP 2OVER DROP (2NIP DROP) (2OVER NIP APPLY TUCK UNIT CONSR SWAP 2SWAP UNFOLDR) IFTE ) (UNFOLDR) PUSH
UNFOLDL |
|
( An…A1 ) B ( C1…Cm) D → ( E1…El )
Left unfold the list A1…An starting from seed B using the operations contained in the list C1…Cm until condition D is satisfied, and push the result list E1…El on the stack. This operation is defined as:
( 2SWAP 2OVER DROP (2NIP DROP) (2OVER NIP APPLY TUCK UNIT CONSL SWAP 2SWAP UNFOLDL) IFTE ) (UNFOLDL) PUSH
APPEND |
|
( A1…An ) ( B1…Bm ) → ( A1…An B1…Bm )
Append two list (right insert). This operation is defined as:
( (DUP NULL =) (DROP) (UNCONSL ROT SWAP CONSR SWAP APPEND) IFTE ) (APPEND) PUSH
PREPEND |
|
( A1…An ) ( B1…Bm ) → (B1…Bm A1…An )
Prepend two list (left insert). This operation is defined as:
(SWAP APPEND) (PREPEND) PUSH
NULL |
|
→ ( )
List constructor (empty list). This operation is defined as:
(( )) (NULL) PUSH
LEN |
|
( A1…An ) → n
Return the length of the list A1…An. This operation is defined as:
((( ) POP) MAP 0 (DROP 1 +) FOLDL) (LEN) PUSH
NTH |
|
( A1…An ) n → ( An )
Return the Nth element of the list (starting from 1 on the left).
This operation is defined as:
( (DUP 1 =) (DROP UNCONSL NIP) (1 - SWAP UNCONSL DROP SWAP NTH) IFTE ) (NTH) PUSH
SEL |
|
( A1…An…Am ) n → ( A1…An-1 An+1…Am ) ( An )
Extract the Nth element from a list. Return the element as well as
the remainder of the list. This operation is defined as:
( NULL -ROT ((DUP 1 =) (DROP UNCONSL) (1 - SWAP UNCONSL -ROT SWAP REC)
IFTE) LAMBDA ((-ROT SWAP DUP NULL =) (DROP SWAP) (CONSL SWAP REC) IFTE) LAMBDA ) (SEL) PUSH
INS |
|
( A1…An-1 An+1…Am ) ( An ) n → ( A1…An…Am )
Insert An in the Nth
position of a list. This operation is defined as:
( SWAP NULL SWAP 2SWAP ((DUP 1 =) (DROP) (1 - SWAP UNCONSL -ROT SWAP 2SWAP SWAP 2SWAP REC) IFTE) LAMBDA ((SWAP DUP NULL =) (DROP) (CONSL REC) IFTE) LAMBDA ) (INS) PUSH
MAP |
|
( A1…An ) ( B ) → ( C1…Cn )
Map the stack B on every element of the list A1…An to obtain another list C1…Cn. This operation is defined as:
( SWAP NULL -ROT ((DUP NULL =) (2DROP) (UNCONSL ROT TUCK APPLY -ROT SWAP REC)
IFTE) LAMBDA ((SWAP DUP NULL =) (DROP) (CONSL REC) IFTE) LAMBDA ) (MAP) PUSH
FOREACH |
|
( A1…An ) ( B ) →
Apply the stack B on each element of a list. This operation is defined as:
( (SWAP DUP NULL =) (2DROP) (UNCONSL ROT TUCK APPLY FOREACH) IFTE ) (FOREACH) PUSH
Conditional
execution is controlled by the primitive IFTE plus other six conditional primitives.
IFTE |
PRIMITIVE |
( A1…An
) ( B1…Bm ) ( C1…Cl ) → B1…Bm | C1…Cl
If-Then-Else control operation. Apply B1…Bm if A1…An return
Boolean true, otherwise apply C1…Cl.
IFT |
|
( A1…An
) ( C1…Cl ) → B1…Bm | NOP
Shorthand for the IFTE operation without the Else branch. This operation
is defined as:
((NOP) IFTE) (IFT) PUSH
= |
PRIMITIVE |
A B → 0 | 1
Conditional EQ. This operation can be applied to numeric as well
as non-numeric elements.
\= |
PRIMITIVE |
A B → 0 | 1
Conditional NEQ. This operation can be applied to numeric as well
as non-numeric elements.
> |
PRIMITIVE |
A B → 0 | 1
Conditional GT (only numeric values).
>= |
PRIMITIVE |
A B → 0 | 1
Conditional GE (only numeric values).
< |
PRIMITIVE |
A B → 0 | 1
Conditional LT (only numeric values).
<= |
PRIMITIVE |
A B → 0 | 1
Conditional LE (only numeric values).
AND |
|
X Y → X&Y
Boolean AND. This operation is defined as:
((0 > SWAP 0 > *) (1) (0) IFTE) (AND) PUSH
OR |
|
X Y → X|Y
Boolean OR. This operation is defined as:
((0 > SWAP 0 > +) (1) (0) IFTE) (OR) PUSH
NOT |
|
X → ~X
Boolean NOT. This operation is defined as:
((0 >) (0) (1) IFTE) (NOT) PUSH
LOOP |
|
(A1…An) (B1…Bm) →
While-loop control. The actions contained in B1…Bm are repeated while the condition identified by A1…An holds true. Like many other, this operation uses LAMBDA recursion and it is defined as:
( (REC) CONSR UNIT SWAP UNIT CONSL (IFT) CONSR LAMBDA ) (LOOP) PUSH
TRUE |
|
→ 1
Boolean true. This operation is defined as:
(1) (TRUE) PUSH
FALSE |
|
→ 0
Boolean false. This operation is defined as:
(0) (FALSE) PUSH
BOOL |
|
A → 0 | 1
Convert top of the stack to Boolean. Note that the language
interprets zero as false and anything else as true. This operation is defined
as:
((0 >) (1) (0) IFTE) (BOOL) PUSH
? |
|
X →
Print out “yes” or “No” if the top of the stack corresponds to
Boolean true or false, respectively. This operation is defined as:
((0 >) ((yes \n) WRITE) ((No \n) WRITE) IFTE) (?) PUSH
Control over
program exceptions is accomplished by the three primitives CATCH, THROW and EXCEPTION.
CATCH |
PRIMITIVE |
( A1…An
) ( B1…Bm ) →
Catch any exception during execution of A1…An. The list B1…Bm is
applied just after exception occurs (i.e. on top of the stack it is the
exception term as described below). Any further exception occurring during
execution of B1…Bm is discarded. If
no exception occurs during execution of A1…An then B1…Bm is
dropped out.
THROW |
PRIMITIVE |
( A1…An
) ( B ) →
Throw exception B with the extra information A1…An. The extra information can contain arbitrary terms, normally
informing about the reason of the error. THROW pushes an exception term on top of the stack, which if uncaught
is raised immediately. Known exception terms (self-explanatory names) include EXISTENCE-ERROR,
PERMISSION-ERROR,
EVALUATION-ERROR,
STACK-ERROR,
IO-ERROR
and SYSTEM-ERROR.
EXCEPTION |
PRIMITIVE |
( A B C
D ( E1…En ) ) →
Identify an exception term. When interpreted (e.g., using APPLY) it prints out the error message, which is the normal behavior
when the exception is uncaught. Otherwise the term is left on the stack and it
will be care of the catcher (see CATCH). In details, A identify the
current input source, B the
corresponding source line, C the calling
context, D the name of the
exception and E1…En is a list of
extra information about the error.
The primitives DEBUG, NODEBUG,
DEBUGGING, TRACE, NOTRACE, TRACING, PROFILE, NOPROFILE, PROFILING, SHOWPROFILE, and RESETPROFILE plus the helper CONTEXT operation identify the (simple)
debugging and profiling capabilities of the language. Given the bottom-up
approach of defining new stacks, reading the full debug output will become soon
unfeasible. Therefore, the user has the opportunity of ignoring a number of
specified operations (via TRACE), whose computation is supposed to give the correct result. Please
note that many programming mistakes can be easily figured out using SEE and/or PS in specific points throughout the code
in order to examine the current stack content. The CONTEXT operation can be used concurrently to examine
the current calling context.
DEBUG |
PRIMITIVE |
Enter debug mode.
NODEBUG |
PRIMITIVE |
Exit debug mode.
DEBUGGING |
PRIMITIVE |
→ 0 | 1
Return a Boolean value according to the current debug mode.
TRACE |
PRIMITIVE |
( B ) →
Start tracing the stack B during debugging. This is the default for all named stacks.
NOTRACE |
PRIMITIVE |
( B ) →
Stop tracing the stack B during debugging.
TRACING |
PRIMITIVE |
( B ) → 0 | 1
Return a Boolean value according to the tracing status of the stack
B.
CONTEXT |
PRIMITIVE |
→ ( A1…An )
Push the full trace of the current calling context on top of the
stack.
PROFILE |
PRIMITIVE |
Start or continue collecting data for the profiler.
NOPROFILE |
PRIMITIVE |
Stop or pause collecting data for the profiler.
PROFILING |
PRIMITIVE |
→ 0 | 1
Return a Boolean value according to the current state of the
profiler.
SHOWPROFILE |
PRIMITIVE |
Print out the data collected so far by the profiler.
RESETPROFILE |
PRIMITIVE |
Reset the profiler (i.e. remove all data collected so far). Note
that the state of the profiler (whether it is collecting data or not) is not
changed by this operation.
This
section covers stacks belonging to libraries useful for various tasks. At this
stage these libraries are directly dependent on the libraries of the hosting
programming language (please see http://www.swi-prolog.org/pldoc/index.html). The
implementation of library applications is a “work-in-progress” process, which
means that some operations might be incomplete. Moreover, the applications are
not fully tested for bugs.
MSB |
PRIMITIVE |
Most significant 1 bit.
LSB |
PRIMITIVE |
Least significant 1 bit.
\/ |
PRIMITIVE |
Bitwise OR.
/\ |
PRIMITIVE |
Bitwise AND.
XOR |
PRIMITIVE |
Bitwise Exclusive OR.
<< |
PRIMITIVE |
Bitwise shift to the left.
>> |
PRIMITIVE |
Bitwise shift to the right.
CEIL |
PRIMITIVE |
Smallest larger or equal integer.
FLOOR |
PRIMITIVE |
Largest larger or equal integer.
TRUNC |
PRIMITIVE |
Truncate to an integer towards zero.
ROUND |
PRIMITIVE |
Round to the nearest integer.
RANDOM |
PRIMITIVE |
Generate a random float in the open domain (0.0, 1.0).
SIGN |
PRIMITIVE |
Sign (integer -1 or +1).
ABS |
PRIMITIVE |
Absolute value.
GCD |
PRIMITIVE |
Greatest common divisor.
DIV |
PRIMITIVE |
Integer division towards -infinity.
REM |
PRIMITIVE |
Remainder of integer division.
MOD |
PRIMITIVE |
Modulo.
^ |
PRIMITIVE |
Power.
+ |
PRIMITIVE |
Addition.
- |
PRIMITIVE |
Subtraction.
* |
PRIMITIVE |
Multiplication.
/ |
PRIMITIVE |
Division.
// |
PRIMITIVE |
Integer division towards zero.
MAX |
PRIMITIVE |
Signed maximum.
MIN |
PRIMITIVE |
Signed minimum.
SQRT |
PRIMITIVE |
Square root.
SIN |
PRIMITIVE |
Sine.
COS |
PRIMITIVE |
Cosine.
TAN |
PRIMITIVE |
Tangent.
ASIN |
PRIMITIVE |
Inverse sine.
ACOS |
PRIMITIVE |
Inverse cosine.
ATAN |
PRIMITIVE |
Inverse tangent.
SINH |
PRIMITIVE |
Hyperbolic sine.
COSH |
PRIMITIVE |
Hyperbolic cosine.
TANH |
PRIMITIVE |
Hyperbolic tangent.
ASINH |
PRIMITIVE |
Inverse hyperbolic sine.
ACOSH |
PRIMITIVE |
Inverse hyperbolic cosine.
ATANH |
PRIMITIVE |
Inverse hyperbolic tangent.
LOG |
PRIMITIVE |
Natural logarithm.
EXP |
PRIMITIVE |
Exponential.
LGAMMA |
PRIMITIVE |
Natural logarithm of the absolute value of the Gamma function.
ERF |
PRIMITIVE |
Error function.
ERFC |
PRIMITIVE |
Complementary error function.
LOG10 |
|
Base-10 logarithm. This operation is defined as:
(LOG 10 LOG /) (LOG10) PUSH
LOG2 |
|
Base-2 logarithm. This operation is defined as:
(LOG 2 LOG /) (LOG2) PUSH
PI |
|
Mathematical constant π. This operation is defined as:
(0.0 ACOS 2 *) (PI) PUSH
E |
|
Mathematical constant e.
This operation is defined as:
(1.0 EXP) (E) PUSH
UMAX |
|
Unsigned maximum. This operation is defined as:
(ABS SWAP ABS MAX) (UMAX) PUSH
UMIN |
|
Unsigned minimum. This operation is defined as:
(ABS SWAP ABS MIN) (UMIN) PUSH
FLOAT |
|
Conversion to float (conversion to integer can be accomplished by
several operations such as TRUNC, ROUND, CEIL, FLOOR). This operation is defined as:
(1.0 *) (FLOAT) PUSH
The system provides standard input/output features. A stream is
represented by a numeric unique identifier.
STDIN |
PRIMITIVE |
Standard input stream.
STDOUT |
PRIMITIVE |
Standard output stream.
STDERR |
PRIMITIVE |
Standard error stream.
INPUT |
PRIMITIVE |
Current input stream.
OUTPUT |
PRIMITIVE |
Current output stream.
OPEN |
PRIMITIVE |
( A ) ( B ) → C
Open the file A with the list of options B and return the stream identifier C. Currently defined options are:
·
READ (default),
WRITE, APPEND
·
TEXT (default),
BINARY
·
FULL-BUFFERING
(default), LINE-BUFFERING, UNBUFFERED
·
CLOSE-ON-ABORT
(default), NOP-ON-ABORT
·
NOP-ON-EOF
(default), ERROR-ON-EOF, RESET-ON-EOF
·
LOCK-NONE
(default), LOCK-READ, LOCK-WRITE
Note that the
filename must be provided in the OS path name convention.
CLOSE |
PRIMITIVE |
C →
Close the stream identified by C.
STREAM |
PRIMITIVE |
C → 0 | 1
Test if C is a valid stream handle.
FLUSH |
PRIMITIVE |
C →
Flush output on stream C.
GET |
PRIMITIVE |
C → X
Read the next character code X from stream C. Return -1 on EOF (if the stream has been opened with the NOP-ON-EOF
option).
PEEK |
PRIMITIVE |
C → X
Read the next character code X from stream C without removing it (i.e. without modifying stream position).
Return -1 on EOF
(if the stream has been opened with the NOP-ON-EOF option). The latter behavior
can be used to test whether the current location is at the end of a stream.
SKIP |
PRIMITIVE |
X C →
Skip input until the next character code X (or EOF) is encountered on stream
C.
PUT |
PRIMITIVE |
X C →
Write the character X on output stream C.
BOF |
PRIMITIVE |
C → M
Reposition the stream C to the beginning of file, returning the new location M.
EOF |
PRIMITIVE |
C → M
Reposition the stream C to the end of file, returning the new location M.
SEEK |
PRIMITIVE |
N C → M
Reposition the stream C by moving of N units (a unit is always 1 byte except for 2-bytes Unicode
encoding) relative to the current position (note that N can be negative), returning the
new location M.
GETS |
|
N C → (A1…An)
Read the next N character codes from stream C.
PUTS |
|
(A1…An) C → N
Write the character codes (A1…An) to stream C.
EMIT |
PRIMITIVE |
A →
Print on standard output the last element on top of the stack.
This
section describe the primitives used to interact with the Windows OS as well as
with the filesystem.
GETENV |
PRIMITIVE |
(A) → B
Get the content of the environment variable A and push it to the default stack
as B.
SETENV |
PRIMITIVE |
B (A) →
Set the content of the environment variable A to B.
FOLDERID |
PRIMITIVE |
(A) → B
Get the value of the standard folder registered with the system
(Vista or later) as A and push
it to the default stack as B. The names of the known folders are defined by the KNOWNFOLDERID
constant (see “Shell Reference” on msdn.microsoft.com).
EXEC |
PRIMITIVE |
(A) →
Execute command A by spawning a Windows task (without waiting for its completion).
SHELL |
PRIMITIVE |
(A) →
Open file or folder A using Windows shell rules.
GETREGKEY |
PRIMITIVE |
(A) → B
Get the value of the registry key A push it to the default stack as B.
SETREGKEY |
PRIMITIVE |
B (A) →
Set the value of the registry key A to B.
DELREGKEY |
PRIMITIVE |
(A) →
Delete the specified registry key A.
GETCWD |
PRIMITIVE |
→ A
Get the current working directory and push it into the default
stack as A.
SETCWD |
PRIMITIVE |
A →
Set the current working directory to A.
DIR |
PRIMITIVE |
A → (B1…Bn)
Get the current working directory content (B1…Bn) after expanding wildcard A.
MKDIR |
PRIMITIVE |
A →
Create the new directory A on the filesystem.
RMDIR |
PRIMITIVE |
A →
Delete the directory A from the filesystem.
DELETE |
PRIMITIVE |
A →
Remove file A from the filesystem.
RENAME |
PRIMITIVE |
A B →
Rename file A as B.
ACCESS |
PRIMITIVE |
A → (B1…Bn)
Get the list of access modes B1…Bn of file/folder A. Currently defined modes are: READ, WRITE, APPEND, EXECUTE and EXIST.
SIZE |
PRIMITIVE |
(A) → B
Get the size of file/folder A (in bytes) and push it to the default stack as B.
TIME |
PRIMITIVE |
(A) → B
Get the time of last modification of file/folder A (as POSIX time, i.e. seconds
elapsed since 00:00:00 UTC of Jan 1, 1970) and push it to the default stack as B.
NOW |
PRIMITIVE |
→ B
Get the current time and push it to the default stack as B.
CTIME |
PRIMITIVE |
B → (C1…C6)
Convert the POSIX time B to a list containing year, month, day, hour, minute, second using
the UTC time-zone (provisional).
-, 32
#, 16
*, 33
/, 31, 33
/\, 31
//, 33
?, 28
?DUP, 17
?INCLUDE, 16
^, 32
+, 32
<, 26
<<, 31
<=, 26
=, 25, 26
>, 26
>=, 26
>>, 31
2DROP, 19
2DUP, 19
2NIP, 20
2OVER, 19
2ROT, 20
-2ROT, 20
2SWAP, 19
2TUCK, 20
ABORT, 15
ABS, 32
ACCESS, 41
ACOS, 34
ACOSH, 34
AND, 26
APPEND, 22
APPLY, 16
ASIN, 33
ASINH, 34
ATAN, 34
ATANH, 34
BOF, 38
BOOL, 27
CATCH, 28
CEIL, 31
CLOSE, 37
CLR, 15
CONSL, 21
CONSR, 20
CONTEXT, 30
COS, 33
COSH, 34
CTIME, 41
DEBUG, 29
DEBUGGING, 29
DELETE, 41
DELREGKEY, 40
DEPTH, 15
DIR, 40
DIV, 32
DROP, 17
DUP, 17
E, 35
EMIT, 39
EOF, 38
ERF, 35
ERFC, 35
EXCEPTION, 28
EXEC, 39
EXIT, 15
EXP, 34
FALSE, 27
FLOAT, 36
FLOOR, 31
FLUSH, 37
FOLDERID, 39
FOLDL, 21
FOLDR, 21
FOREACH, 25
GCD, 32
GET, 37
GETCWD, 40
GETENV, 39
GETREGKEY, 40
GETS, 38
IFT, 25
IFTE, 25
INCLUDE, 15
INCLUDED, 16
INPUT, 36
INS, 24
LAMBDA, 16
LEN, 23
LGAMMA, 35
LOG, 34
LOG10, 35
LOG2, 35
LOOP, 27
LSB, 31
MAP, 24
MAX, 33
MIN, 33
MKDIR, 40
MOD, 32
MSB, 30
NIP, 18
NODEBUG, 29
NOP, 16
NOPROFILE, 30
NOT, 27
NOTRACE, 29
NOW, 41
NTH, 23
NULL, 23
OPEN, 37
OR, 26
OUTPUT, 37
OVER, 18
PEEK, 38
PI, 35
PICK, 17
POP, 14
PREPEND, 23
PROFILE, 30
PROFILING, 30
PUSH, 14
PUT, 38
PUTS, 39
RANDOM, 32
REC, 16
REM, 32
RENAME, 41
RESETPROFILE, 30
RMDIR, 40
ROLL, 17
ROT, 18
-ROT, 18
ROUND, 31
SEE, 15
SEEK, 38
SEL, 24
SETCWD, 40
SETENV, 39
SETREGKEY, 40
SHELL, 39
SHOWPROFILE, 30
SIGN, 32
SIN, 33
SINH, 34
SIZE, 41
SKIP, 38
SQRT, 33
STDERR, 36
STDIN, 36
STDOUT, 36
STREAM, 37
SWAP, 18
TAN, 33
TANH, 34
THROW, 28
TIME, 41
TOP, 15
TRACE, 29
TRACING, 29
TRUE, 27
TRUNC, 31
TUCK, 19
UMAX, 36
UMIN, 36
UNCONSL, 21
UNCONSR, 21
UNFOLDL, 22
UNFOLDR, 22
XOR, 31
Last modified on Tuesday, March 3, 2015 by Mauro DiNuzzo