<A name="MARKSEC35"></A>
This section contains information about
how to modify Aida's behavior;
how to write dynamic contents in an Aida file;
* how to extend Aida and develop a
converter for a new target.
<A name="MARKSEC36"></A>
The Aida system is essentially made of two components:
a core application (the aida command itself) written in C;
a library written in
Tcl
which contains definitions for most of the tasks to perform.
The core component is just a driver for the library: it contains a parser
for the Aida Markup Language and a Tcl interpreter to execute the code in
the library. It is responsible for calling the right code at the
appropriate moment. Technically, the parser is based on a strict LALR(1)
grammar and is implemented with the help of the Bison and Flex programs.
The library is where the conversion procedures corresponding to the
various tags of the Aida Markup Language are defined. Since it is written
in Tcl, with some basic knowledge of this language, one can understand the
code and modify it if necessary. Tcl is an easy to learn, yet extremely
powerful, language.
The basic library files are located in the directory $aida_library/base. For
instance:
$aida_library/base/default.tcl
To know the location of this library, one can execute the command
aida info library
The default location on a Unix system, as of version 1.0,
is '/usr/local/share/aida/1.0'.
This location can be changed if necessary (see the variable
aida_path below).
<A name="MARKSEC37"></A>
Here is the current structure of the Aida library (as of version 1.0):
--- library |--- base | |--- convert.tcl | |--- default.tcl |--- callbacks.tcl |--- core.tcl |--- debug.tcl |--- html | |--- convert.tcl | |--- default.tcl |--- hyperref | |--- convert.tcl | |--- default.tcl |--- latex | |--- convert.tcl | |--- default.tcl |--- man | |--- convert.tcl | |--- default.tcl |--- markdown | |--- convert.tcl | |--- default.tcl |--- mediawiki | |--- convert.tcl | |--- default.tcl |--- pmwiki | |--- convert.tcl | |--- default.tcl |--- template | |--- convert_tmpl.tcl | |--- default_tmpl.tcl |--- text | |--- convert.tcl | |--- default.tcl |--- trac | |--- convert.tcl | |--- default.tcl |--- utils.tcl
Each target directory has two files :
convert.tcl contains the definition of the callbacks;
default.tcl contains default values for some header
parameters and taget-specific variables.
<A name="MARKSEC38"></A>
The inner mechanism of Aida relies on callbacks which are invoked from the
core at the appropriate moment and which take care of providing the output
corresponding to the various tags of the Aida Markup Language. For instance, when
the aida parser meets the ((table
tag, it automatically invokes a
Tcl proc named tableProc: this proc receives all the necessary
elements, like attributes and list of rows, and then is responsible for
building the table in the target format.
These callbacks are Tcl procs defined in a separate file for each target.
This file is named convert.tcl and is located in a directory named
like the target. For instance, the procs for the html target are stored in
$aida_library/html/convert.tcl
The procs are defined in a namespace with the same name as the target. For
instance, if the target is html, all the procs are defined in the html
namespace, so the table proc is named html::tableProc.
All the procs have a default definition found in the base directory of the
Aida library. There is an automatic fallback mechanism: if a target
specific proc, like html::tableProc, is not provided for the html
target, then the default definition is used instead.
<A name="MARKSEC39"></A>
The Aida Tcl interpreter defines a few global variables which can be
useful for a user in order to control or modify Aida's behavior in a
custom script, or for a developer in the definition of a new target.
The following global variables are currently available:
<TABLE border="1" align="center"> <TR><TD>*aida_library*</TD><TD>the location of the library directory</TD></TR> <TR><TD>*aida_path*</TD><TD>the list of all the directories visited to find targets</TD></TR> <TR><TD>*aida_verbosity*</TD><TD>the current level of verbosity</TD></TR> <TR><TD>*aida_version*</TD><TD>the current version of Aida</TD></TR> <TR><TD>*aida_cwd*</TD><TD>the current working directory</TD></TR> <TR><TD>*aida_target*</TD><TD>the current target</TD></TR> <TR><TD>*aida_name*</TD><TD>the name of the input file (if any)</TD></TR> <TR><TD>*aida_mapping*</TD><TD>a logical value to control chars mapping</TD></TR> <TR><TD>*aida_unwrap*</TD><TD>a logical value to control text wrapping</TD></TR> </TABLE>The variables aida_library, aida_verbosity, aida_mapping and aida_unwrap
are linked variables. This means that any change made to them from the Tcl
code modifies them accordingly in the C code.
&``apos;
.Some of these variables are set by the core before the default.tcl
files in the configuration directories are sourced, so their values can be
overriden there.
<A name="MARKSEC40"></A>
Parameters governing Aida's behavior can be set at different levels:
on the command line
in the input file's header
in the configuration files
in the library files containing the callbacks
This list indicates the order of precedence. A command line option has
precedence over any settings made in the header of the input file. The
latter have precedence over settings found in the configuration files,
which in turn have precedence over settings made in the library source
files.
Concerning library files, a target specific definition has precedence over a
global one: if the target is html, a variable defined in
$aida_lib/html/default.tcl overwrites a variable set in
$aida_lib/base/default.tcl.
Among the configuration files, per-user settings have precedence
over site-wide settings.
Here are some examples demonstrating how to set the PageWidth
header parameter at different levels:
* in the header of the input file:
:PageWidth: 80
in a configuration file, with a Tcl instruction like
this:
set aida_head(PageWidth) 80
If the setting concerns the text target exclusively, the instruction is:
namespace eval text { variable aida_head set aida_head(PageWidth) 80 }
<A name="MARKSEC41"></A>
The pair of tags ((e e))
can evaluate any valid Tcl code during the
conversion process. This is very useful for generating contents on the fly,
i-e when a file is processed. The result of the Tcl code is inserted in the
document in place of the ((e e))
tags and is passed to the Aida
parser.
Combining this possibility with the :Source: or :TclCmd: header
parameters, or with the -prefix command line option provides with a very flexible
system to generate dynamic contents.
Let us start with a simple example. The following piece of text
will display the name of the currently logged user:
This is the session of user ((e exec whoami e)).
As a result, the converted file will display:
This is the session of user bernardo.
Indeed exec is a Tcl command used to execute a Unix command and whoami is
the Unix command reporting the name of the user of the current session.
The same Tcl interpreter is used by Aida to evaluate any piece of
code so one can, for instance, define a global variable somewhere and reuse
it later. Let us see a simple example making use of the :TclCmd:
header parameter. The next header instruction creates a global
variable symphNum:
:TclCmd: set symphNum 5
Then the Aida document can use it like this:
Beethoven's symphony #((e set symphNum e))
which will result in Beethoven's symphony #5.
The ((e e))
tags can contain several lines of Tcl
instructions, i-e a complete Tcl script. They can also contain the name of
a proc which has been previously defined. For instance, here is a proc,
written in Tcl,
detecting the presence of a Music directory in the user's home
directory and counting the number of albums:
proc countAlbums {} { set musicDir [file normalize ~/Music] if {[file exists $musicDir]} { set albums [glob -dir $musicDir -type d *] set num [llength $albums] return "contains $num albums" } else { return "is missing" } }
The proc can be used like this:
The Music dir ((e countAlbums e)).
which results in something like: The Music dir contains 4 albums.
This will work only if the countAlbums proc is found by the Tcl
interpreter. There are several methods to achieve this:
the definition of the proc can be placed somewhere (before it is used)
in a ((e e))
pair of tags; the proc can be stored in a file which is sourced using the
:Source: header parameter. For instance, suppose the proc is saved in a
file named myProcs.tcl located in the same folder as the Aida file.
It is enough then to write the following header instruction at the
beginning of the Aida file:
:Source: myProcs.tcl
same as before but the file is sourced using the -prefix command line
option. The command line would look like this:
aida convert -targ html -prefix myProcs.tcl foobar.aida
The solution of storing the proc in a separate file is the most
convenient because it allows you to define an entire library of Tcl procs
and maintain them in a single location. The same Tcl file could also be used
in other Aida documents.
Here is a last example demonstrating the generation of more complex
Aida code. The proc randomTable constructs a table with a specified
number of rows and columns in which each cell contains a random number.
Here is the proc (which uses the rand() function to generate the random
numbers):
proc randomTable {nrow ncol} { set result [list "((table border=1 align=center"] for {set i 0} {$i < $nrow} {incr i} { set cells [list] for {set j 0} {$j < $ncol} {incr j} { lappend cells [string range [expr rand()] 0 5] } lappend result "((tr [join $cells \t] tr))" } lappend result "table))" return [join $result \n] }
Let us now generate such a table with 3 rows and 4 columns with the
following instruction:
((e randomTable 3 4 e))
The Tcl proc
will generate the following code:
((table border=1 align=center ((tr 0.2488 0.2179 0.7435 0.1301 tr)) ((tr 0.7191 0.2883 0.9111 0.3472 tr)) ((tr 0.9798 0.0362 0.7835 0.8275 tr)) table))
This is a block of code written in the Aida Markup Language which will be
processed by the Aida parser and will finally result in the following
table:
<A name="MARKSEC42"></A>
The Aida Tcl interpreter defines a few core commands which can be used from
library code or from custom scripts in order to interact with the program.
These core commands are described in the following sections. They are all
defined in the aida namespace.
<A name="MARKSEC43"></A>
This command does not take any argument. It returns a boolean which
indicates if Aida is in the process of splitting (value 1) or of converting to a
single output file (value 0).
<A name="MARKSEC44"></A>
The syntax of this command is:
aida::getDepth ?type?
When it is used without any argument, it returns the current depth of nested
lists. If the parser is not currently in a list block, the value is 0.
The type argument can be "dl", "ol" or "ul" which stands for
description list, ordered list and unordered list respectively. In that
case, the command returns the depth among lists of the given type : if an
ordered list contains an unordered list which itself contains an ordered
list, the depth (among ordered lists) inside the innermost list is 2 and
not 3, and this is the value which is returned by [aida::getDepth "ol"].
<A name="MARKSEC45"></A>
This command does not take any argument. It returns the path of the
directory containing the Aida file which is currently parsed. This file is
not necessarily the top file because it could have been included with an
((input
or an ((include
tag.
If the Aida input comes from stdin, the returned path is the
current directory.
<A name="MARKSEC46"></A>
The syntax of this command is:
aida::inputEncoding ?enc?
It lets you get or set the input encoding. When used with no argument, it
returns the name of the current input encoding. Otherwise, the command sets
the input encoding to enc.
Not all the encodings returned by the Tcl command [encoding]
names can be used. See the command aida info encodings: it
returns a list of the known encodings but some of them are marked with an
asterisk indicating that they are not available as input encoding (only as
output encoding). Currently dingbats, ebcdic, identity, symbol, unicode (i-e UTF16) are not supported. This
restriction is due to the fact that the Aida Bison parser can not handle
these encodings. It might be removed in a future version of Aida.
The UTF8 encoding is supported and can be specified as "utf-8" or "utf8".
<A name="MARKSEC47"></A>
The syntax of this command is:
aida::outputEncoding ?enc?
It lets you get or set the output encoding. When used with no argument, it
returns the name of the current output encoding. Otherwise, the command
sets the output encoding to enc.
All the encodings returned by the Tcl command [encoding]
names can be used as output encoding.
<A name="MARKSEC48"></A>
Defining a new target amounts essentially to two tasks:
create a directory with the name of the target, containing two files
named convert.tcl and default.tcl;
write the callbacks for your target in the file convert.tcl and
set the value of some internal variables in the file default.tcl
if necessary.
The directory for the new target can be located anywhere on the host
machine provided this location is listed in the aida_path variable
so that the aida command can find it.
A possible solution is to create a custom library and
install the target in this library. For instance, suppose this custom
library is named aidalocal in your home directory and you want to
define a new target for the hypothetical format foobar.
You just have to create the following architecture:
--- aidalocal |--- foobar |--- convert.tcl |--- default.tcl
In order to make sure that Aida detects this target, the path of the aidalocal library must be appended to the aida_path variable.
The Tcl instruction to do that is simply
lappend aida_path ~/aidalocal
This instruction should be inserted in your configuration file ~/.aidarc/config.tcl (create it if it does not exist yet). See the
section Configuration files for more info about this. See the
section about the Global variables for more information about the aida_path variable.
Once this is done, the following command should list the new target :
aida info targets
The following sections explain how to define the callbacks invoked by the
Aida parser when the commands aida convert or aida split
are executed.
As a convenience, there are template files in the Aida library for
the two target files convert.tcl and default.tcl. They are
located in the subdirectory template of the Aida library (you can
find this library with the command aida info lib). These templates
are named convert_tmpl.tcl and default_tmpl.tcl. You can
just copy them to your new target directory (~/aidalocal/foobar) and remove the _tmpl
suffix: then edit them and replace all the occurences of the string <TMPL>
by the name of your target (foobar in our previous example) as explained at
the beginning of the template files.
All that remains to do is to provide sensible definitions for the
callbacks. Not all callbacks have to be defined. Aida provides simple
basic definitions for the callbacks which may be enough for the new
target. These definitions are invoked automatically by Aida when no target
specific callback is found.
For instance, if the parser invokes the commentProc callback
and if no proc named foobar::commentProc is found by the
interpreter, then the default definition (named base::commentProc)
is automatically invoked instead.
In order for this automatic mechanism to work correctly, it is
essential that you place the following instruction at the beginning of the
file convert.tcl:
namespace eval foobar { # Ensure fallback on base commands namespace path ::base }
Note that this piece of code is provided by the template file, so, if you
applied the instructions above, there is nothing to do.
All the definitions and settings are done in a namespace with the
same name as the target. If the target is foobar, the previous
instruction had the effect of creating a namespace named foobar.
<A name="MARKSEC49"></A>
As explained in the previous section, the target directory contains a file
convert.tcl with the definitions of the callbacks and a file default.tcl with target specific settings (header parameters,
attributes, etc.).
The following procs may be defined in the target namespace:
anchorProc commentProc horizRuleProc imageProc linkProc listProc navBarProc newLineProc postambleProc preambleProc printIndexProc refProc sectionProc setIndexProc styleProc tableProc tocProc verbProc verbatimProc
For instance, in the case of the html target, this should be
really: html::anchorProc, html::commentProc, etc.
The precise syntax of the callbacks is explained in the following
paragraphs. In order to get a better understanding of what these procs are
supposed to do, it can be useful to look in the Aida library and read the
definitions of the various targets. The file utils.tcl in the
library also contains various utility procs which can help in writing the
callbacks. These procs are documented in the comments accompanying their
definition.
The following sections indicate the prototype of each callback. The
core is responsible for invoking the callbacks at the appropriate moment
and for filling the arguments accordingly. Everywhere, in what follows, trgt should be replaced by the actual name of the target.
<A name="MARKSEC50"></A>
The syntax of the anchorProc callback is:
proc trgt::anchorProc {label}
This proc must return a string which defines a label or an anchor named
label in the target format.
<A name="MARKSEC51"></A>
The syntax of the commentProc callback is:
proc trgt::commentProc {str}
This proc must return a string which represents a comment with the
str argument in the target format.
<A name="MARKSEC52"></A>
The syntax of the horizRuleProc callback is:
proc trgt::horizRuleProc {}
This proc receives no argument and must return a string which
represents a horizontal rule in the target format.
<A name="MARKSEC53"></A>
The syntax of the imageProc callback is:
proc trgt::imageProc {str attr}
This proc must return a string which represents the code to insert an image
in the target format. The str argument is the address of the image
and the attr argument is a (possibly empty) set of attributes.
The basic image attributes are:
_height _: numeric value indicating the height of the image.
width : numeric value indicating the width of the image.
_align_: alignment of the table. Possible values: center, left or
right.
alt: a string argument to provide a caption for the image.
Some targets support other attributes.
For instance, with the html target, one can specify any of the
attributes supported by the Html <IMG> tag.
With the latex target, one can specify a clip attribute
indicating if the image should be clipped to the specified dimensions. lt))
<A name="MARKSEC54"></A>
The syntax of the linkProc callback is:
proc trgt::linkProc {str url}
This proc must return a string which defines an external hyperlink named
str in the target format. The url argument is the
destination this link is pointing to.
<A name="MARKSEC55"></A>
The syntax of the listProc callback is:
proc trgt::listProc {type depth attr itemList}
This proc must return a string which represents the code for an entire
list in the target format.
The itemList argument is a Tcl list containing all the list
items.
The type argument can be on of "ol", "ul" or "dl",
designating respectively an ordered list, an unordered list or a
description list.
The depth argument indicates the nesting level of the list:
a list at the top level has depth 1. If one of its items contains a list
itself, this list will have depth 2. One can use the core command aida::getDepth
to have more information about the depth.
The attr argument is a (possibly empty) set of attributes.
The basic list attributes are similar to those found in the Html language:
_start _: it concerns ordered lists and indicates the first value of the numbering
type: it concerns both ordered and unordered lists.
- In the case of an
unordered list, it specifies the kind of symbol to use at the beginning of
a list item (possible values: disc, square or circle).
- In the case of an
ordered list, it specifies the kind of numbering at the beginning of
a list item (possible values: "1" for arabic numbers, "A" for uppercase
letters, "a" for lowercase letters, "I" for uppercase roman numbers, "i"
for lowercase roman numbers).
Some targets support other attributes. For instance, with the html
target, one can specify any of the attributes supported by the Html <OL>,
<UL> or <DL> tags.
<A name="MARKSEC56"></A>
The syntax of the newLineProc callback is:
proc trgt::navBarProc {curr prev next top}
This proc must return a string which defines a navigation bar in the target
format. It is invoked only in the case of the aida split command.
Its arguments curr, prev, next, top correspond respectively to the
current split file, the previous one, the next one and the top one.
<A name="MARKSEC57"></A>
The syntax of the newLineProc callback is:
proc trgt::newLineProc {}
This proc receives no argument and must return a string which
represents a line feed in the target format.
<A name="MARKSEC58"></A>
The syntax of the postambleProc callback is:
proc trgt::postambleProc {}
This proc receives no argument and must generate a block of text to be
written at the end of the output file in the target format.
<A name="MARKSEC59"></A>
The syntax of the preambleProc callback is:
proc trgt::preambleProc {}
This proc receives no argument and must generate a block of text to be
inserted at the beginning of the output file in the target format.
<A name="MARKSEC60"></A>
The syntax of the printIndexProc callback is:
proc trgt::printIndexProc {}
This proc receives no argument and must generate a block of text
representing the list of all the terms indexed in the original Aida file
using the ((x x))
pair of tags.
<A name="MARKSEC61"></A>
The syntax of the refProc callback is:
proc trgt::refProc {str label {file ""}}
This proc must return a string which defines an internal hyperlink or
reference in the target format.
The str argument is the string to be displayed. The label
argument is the label this reference is pointing to.
The file argument is filled only in the case of the aida
split command and contains the name of the split file where the label
is found.
<A name="MARKSEC62"></A>
The syntax of the sectionProc callback is:
proc trgt::sectionProc {str level {file ""}}
This proc must return a string which defines a section in the target format.
The str argument is the title of the section and the level argument is its level (a number between 1 and 6).
The file argument is filled only in the case of the aida
split command and contains the name of the split file where the section
is found.
<A name="MARKSEC63"></A>
The syntax of the setIndexProc callback is:
proc trgt::setIndexProc {str {file ""}}
This proc must return a string which can generate an indexed entry in the
target format if this format supports indexation. Some target
implementations (like the text target for instance) manage their index
entries internally and this proc may return an empty string.
The str argument is the name of the entry.
The file argument is filled only in the case of the aida
split command and contains the name of the split file where the entry
is found.
<A name="MARKSEC64"></A>
The syntax of the styleProc callback is:
proc trgt::styleProc {style begin}
This proc must return a string which generates a switch to or from the
specified style.
The 'style' argument can be: "i", "b", "u".
The 'begin' argument tells if it is an opening or a closing tag.
<A name="MARKSEC65"></A>
The syntax of the tableProc callback is:
proc trgt::tableProc {attr rowList}
This proc must return a block which represents an entire table.
The rowList argument is a Tcl list containing all the rows of the
table. Each row is a string in which the cells are separated by a
tabulation character.
The attr argument is a (possibly empty) set of attributes.
The basic table attributes are:
_border _: numeric value indicating the width of the border (0 means no border)
align: alignment of the table. Possible values: center, left or
right.
_format*_: alignment of the columns. A Latex-like specification as with the
\tabular
environment. For instance: format=cclr
.
Some targets support other attributes.
For instance, with the html target, one can specify any of the
attributes supported by the Html <TABLE> tag.
<A name="MARKSEC66"></A>
The syntax of the tocProc callback is:
proc trgt::tocProc {}
This proc receives no argument and must generate a block of text
representing the table of contents of the document.
<A name="MARKSEC67"></A>
The syntax of the verbProc callback is:
proc trgt::verbProc {str}
This proc is invoked when the ((v v))
or the ((q q))
pair
of tags are encountered. It must return a string which represents an
inline verbatim piece of text corresponding to the str argument in
the target format.
<A name="MARKSEC68"></A>
The syntax of the verbatimProc callback is:
proc trgt::verbatimProc {str}
This proc must return a string which defines a verbatim block in the
target format. The str argument contains the text to display.
<A name="MARKSEC69"></A>
A hook is just a Tcl proc which is executed at the appropriate moment.
These hooks are meant mainly for developers who define a new target
and want to perform some tasks related to the target (pre- or
post-treatment). If a user wants to perform similar tasks, the best way to
achieve this is to use the :Source: or :TclCmd: header
parameters or the -prefix option.
<A name="MARKSEC70"></A>
When
one of the commands aida convert or aida split is
executed, two hooks can be defined:
the pre convert hook is executed at the end of the startup
sequence, when the Aida input file is about to be parsed; the post convert hook is executed at the end of the parsing
process.
The pre convert hook is a proc named preConvertHook
and
defined in the namespace of the target. For instance, if the target is
html, the pre convert hook proc should be named html::preConvertHook
.
Similarly, the post convert hook proc is named postConvertHook
in
the same namespace. With the html target, its name should be html::postConvertHook
.
The preConvertHook
and postConvertHook
procs do not
take any argument. For instance, with the html target, the prototypes are:
proc html::preConvertHook {} { # Definition here } proc html::postConvertHook {} { # Definition here }
<A name="MARKSEC71"></A>
In the case of the aida split command, there is an
additional hook named splitHook
which is executed just after a new
split file has been created and before any content is written in it. The
splitHook
proc takes one parameter which is the name of the split
file. For instance, with the html target, the prototype is:
proc html::splitHook {file} { # Definition here }