Program structure
This part should give an idea of the over-all look of a Shapes program. Only the first section below is defining the language; the subsequent sections are just advisory.
The Shapes source shall be encoded using utf-8, and use UNIX style line endings (that is, the new line character alone, byte value 10).
Shapes source is not sensitive to indentation. However, any indentation should be made using the space character only. The tab character is reserved to support indentation of text presented with variable-width fonts.
The set of global bindings available to a Shapes program consists of two parts. First, there are the kernel bindings, set up by binary code in the compiler alone. Second, the prelude adds bindings defined in the Shapes language. The prelude resides in the parent environment to the environmet of the input source file, and the kernel bindings reside in the prelude's parent environment.
While the kernel bindings are uniquely defined by the compiler version, the exact contents of the prelude depends on the user's setup. However, there comes a standard prelude with the compiler, so if the user makes no changes to the default, the distinction between kernel bindings and the prelude is of minor importance — except for where to find documentation.
The set of files to evaluate in order to set up the prelude is determined by the
Shapes-Namespace.txt files that appear in every directory in the need search path (see
syntax.html and
man page).
The standard extensions that are part of the standard prelude are marked clearly in the documentation, so these need not be included explicitly.
The Shapes compiler will run either in
shape mode or in
blank mode, typically determined by the suffix of the input filename.
Shape mode is selected for input files with suffix
.shapes, or when in doubt.
In shape mode, the only necessary aspect to discuss regarding the global structure of a Shapes program is how the graphical output is defined. There are three means for defining the output of a Shapes program:
- Through the value of the program as an expression. If the program results in a non-§Void value, it must be a §Drawable, and not a null-picture. The result will be a single-page document.
- Through a non-empty final value of ..Shapes..IO..•page. For single-page documents. This is equivalent to ending the program with the expression (..Shapes..IO..•page).
- Through a non-empty final value of ..Shapes..IO..•catalog. For multi-page documents.
At least one of the alternatives must be used by the program. It is, however, not necessary that exactly on is used; some combinations are OK due to precedence rules.
Blank mode is selected for input files with suffix
.blank.
In blank mode, the Shapes compiler is not expecting a graphical result to be defined, and will not write any results to output files. In this mode, the program result is not in any standardized format. One common case is a program which just produces output on stdout.
This section gives general recommendations on how to organize Shapes source code.
One-liners
The shortest non-empty valid Shapes program I can think of (an empty program is valid in blank mode), is
Shapes..@spot
It produces a circular mark.
This program has the typical structure of a one-liner; it consists just of a pure expression. Being a one-liner, it doesn't contain any
##lookin lexer directive, so the
Shapes namespace needs to be prepended to all standard bindings.
A nice application of this is to produce a small label typeset using LaTeX, as shown by the example below.
One-liner |
---|
|
A typical Shapes program.
|
|
Source:
show/hide
—
visit
|
[Shapes..Graphics..TeX `$x^{2}$´]
|
Note that one-liners are often given to the compiler through the
stdin stream. Search for
stdin on
man.html for more information.
A typical Shapes program
Let us discuss the big picture of a typical
shape mode program. (Recall that shape mode means that compilation of this program will result in graphics.)
First, a typical program will list a couple of standard extensions, with the lexer directive ##needs.
##needs ..Applications..Blockdraw / blockdraw
If one thinks the font size of TeX labels is too small, this could be a good thing to change next. One may also want to add some LaTeX packages…
##classoption 10pt
##preamble \usepackage{bm}
There are a couple of other lexer directives that could go here, but one being of particular importance is the one which tells in which namespaces to look for identifiers. With these we will be able to write just ignore instead of Shapes..ignore, and longblock instead of Applications..Blockdraw..longblock.
##lookin ..Shapes
##lookin ..Applications..Blockdraw
The leading namespace separator in the ##lookin lexer directive isn't necessary in most cases, but it is recommended to use it anyway to allow any namespaces that really should be given as relative to stand out.
Now, say we don't like some of the default settings of dynamic variables. Then we encapsulate the rest of the program with a couple of dynamic bindings in scope.
@longblockrx:10mm
& @connectionlw:0.3bp
|
{
/** rest of program goes here, typically without indentation. **/
}
Finally, the code that really produces something is inserted where the dynamic bindings are in scope. As a very simple example, one might insert code like this:
a: [putblockOrigin •page [longblock [TeX `$\bm{a}$´]]]
b: [putblockFarRight •page [longblock [TeX `$\bm{b}$´]] a]
ignore [] [connect •page a b]
Unless something is out of sync, the whole program is included in the example below.
Typical shape mode program |
---|
|
A typical Shapes program.
|
|
Source:
show/hide
—
visit
|
##needs ..Applications..Blockdraw
##classoption 10pt
##preamble \usepackage{bm}
##lookin ..Shapes
##lookin ..Applications..Blockdraw
@longblockrx:10mm
& @connectionlw:0.3bp
|
{
a: [putblockOrigin IO..•page [longblock [Graphics..TeX `$\bm{a}$´]]]
b: [putblockFarRight IO..•page [longblock [Graphics..TeX `$\bm{b}$´]] a]
ignore [] [connect IO..•page a b]
}
|
A blank mode Shapes program
Sometimes, one may just be interested in playing around with programming concepts and produce text output to verify the operation. For instance, say that we would like to show somebody how a factorial function can be defined;
factorial: \ n → [if ( n = '0 ) '1 ( n * [factorial n-'1] )]
Shapes..•stdout << [factorial '5] << "{n}
In shape mode, this would not be a valid program since no graphics is produced. The reason is that Shapes is primarily designed for graphics tasks, and in shape mode it should generally be considered an indication of failure if no graphics is produced.
To solve the problem, the primary option would be to run the program in
blank mode, by naming the input file with the
.blank suffix.
The secondary option would be to end the program with any simple expression that evaluates to a
§Drawable, for instance
@spot /** Avoid empty-output error in shape mode. **/
The comment makes it clear that the expression has nothing to do with the main purpose of the program. One advantage of this solution is that the program will work both in shape mode and in blank mode. The complete program is given in the example below.
Text output in shape mode |
---|
|
A text-producing Shapes program in shape mode must produce graphics as well.
|
|
Source:
show/hide
—
visit
|
##lookin ..Shapes
factorial: \ n → [if ( n = '0 ) '1 ( n * [factorial n-'1] )]
IO..•stdout << [factorial '5] << "{n}
Graphics..@spot /** Avoid empty-output error in graphics mode. **/
|
|
stdout:
show/hide
|
'120
|