Just Another Macro Language v3.01

. . . . . . . . . . . . . . . . . . . . . . . . for HTML

Writing j-SEX Modules


Table of Contents


1. General introduction

After V1.0 was released there was a huge demand for several extensions. Users wanted to have macros that could not be defined using 's define macro but are too specific to include them into the language. Some user even modified the source of .

On the other hand user modification of would lead to several incompatible versions. To avoid this V2.0 introduced j-SEX, which stands for Simple EXtension and means extension modules for written in Perl.

This feature should be powerful enough to serve all needs, and there is no user need anymore to modify the source code. (See 7. Modifying the source of . NO!)

Writing j-SEX modules is a powerful way to extend the built-in features of . However to do this you should be familiar with the language Perl, Perl modules and . If you are lucky enough to read Hungarian text you can learn Perl from my short Perl booklet. If you first language is English, then you do not need my book anyway.

A j-SEX module for is a Perl module file under the library directory jamal. This module is included into the program using the Perl operator use when evaluates the a macro having the same name (i.e.: use).

The function BEGIN of the module can contain code to define the "user defined" macros of the module that the users can redefine to fine tune the behaviour of the macros. Also BEGIN should contain code to ease version maintenance (See details later).

The functions of the module define the macros implemented by the module. Each function corresponds to a macro. Whenever finds a macro which should have been implemented in this module it tries to call the function of the same name. In the example.pm extension module there is a function called licence. This can be referenced in invoking the macro


     {#example::licence Here is a Good Test}

The function END is called when is exiting. This is the normal behaviour of the Perl interpreter. The function END therefore can be used to finalize outputting and closing tasks that remained during processing. There is no example for this usage in example.pm


2. An example

version 2.0 and later versions are shipping the example j-SEX module example.pm. The text of this module:

001 #
002 # PACKAGE EXAMPLE
003 #
004 
005 # This is a Perl defined macro extension module. This file should be placed into the
006 # directory named 'jamal' under the Perl Module library directory. This is
007 # C:\Perl\lib\jamal on my Windows NT. On UNIX this is something different.
008 =pod
009 =H Example Perl written extension module for jamal
010 =abstract
011 This is a sample extension module for jamal written in Perl. Learn this module
012 or use as a starting point to create your own modules.
013 =end
014 
015 This module is not intended for serious Perl usage. This is intended as a learning tool
016 or as a starting point to develop Perl written jamal extensions.
017 
018 This module is a jamal compliant extension that tries to show all features of the
019 jamal interface that jamal provides for its extensions.
020 
021 See the original source code of this module for explanatory comments.
022 =cut
023 
024 package jamal::example;
025 
026 BEGIN {
027 $VERSION = '2.0'; #the current module version
028 &jamal::require('2.0');#require jamal version
029 $RVERSION = &jamal::version('1.0','2.0');
030 $RVERSION = $VERSION unless $RVERSION;
031 # define package specific macros
032 if( $RVERSION > 1.0 ){
033   &jamal::DefineMacro( 'cap' , ',x' , '<font size=+1>x</font>' );
034   }
035 }
036 
037 # Create a licence text. All characters are capital, but those
038 # that are capital in the original text are typesetted using
039 # larger font. Version 1.0 of this module used fix <font size=+1>x</font>
040 # enlarging scheme. Version 2.0 already uses the macro {::cap/x}
041 # and the user can redefine it to have different intials. However to
042 # be 100% compatible with old version if version 1.0 is requested
043 # we still generate the old <font size=+1>x</font> sequence.
044 sub licence {
045   my $text = shift;
046   my $i;
047   my $output='';
048 
049   $text =~ s/^\s*//;
050 
051   for( $i = 0 ; $i < length $text ; $i++ ){
052     my $c = substr($text,$i,1);
053     if( $c le 'Z' && $c ge 'A' ){
054       if( $RVERSION > 1.0 ){
055         $output .= &jamal::Open . "example::cap/$c" . &jamal::Close; 
056         }else{#remain compatible vith older version
057         $output .= "<FONT SIZE=+1>$c</FONT>"; 
058         }
059       }
060     elsif( $c le 'z' && $c ge 'a' ){ $output .= uc $c }
061     else{ $output .= $c; }
062     }
063   return $output;
064   }
065 
066 # just to demonstrate the call of &jamal::verbatim
067 sub list {
068   my $param = shift;
069   $fn = &jamal::input($param);
070   my $output = '';
071   return "***$fn can not be opened!!!***"  unless open(F,"<$fn");
072   my $i = 1;
073   while( <F> ){
074     s/\&/&amp;/g;
075     s/\</&lt;/g;
076     $output .= sprintf('%03d ',$i) . $_;
077     $i++
078     }
079   &jamal::verbatim;
080   return $output;
081   }
082 
083 END {
084 
085   }
086 1;
This module is for example purposes and not really ment to be used in real word projects. The module uses the BEGIN function to maintain version information as well as to define module specific user defined macros.

Later it implements macros licence and list. The function BEGIN contains code for the version control. It sets the module variable $VERSION to 2.0 which is the current version of the extension module, and stores the version requested by the macro file in the module variable $RVERSION.

Calling the function jamal::version in this example has two effects. It tells that the module can handle the features of this module from version 1.0 upto version 2.0. If a version below 1.0 or above 2.0 is requested generates a fatal error and stops processing. If the version is accepted by it returns the requested version or zero string if no version is requested. By default the module decides that if no version is requested then the version 2.0 is going to be fine for the user. This is the best it can do.

If a different version is requested the module is going to remember to revert its behaviour to its previous version.

This example module has version 2.0, but there was an earlier version 1.0. To be honest this is the first version, but was written to explain versioning.

The macro licence defined by this module behaves slightly different in version 1.0 and 2.0. Keeping the requested version in the variable $RVERSION the module can decide how to implement the function.

The function list implements a macro that opens a file, reads the lines from it, and returns the string converted a bit. The conversion changes all & character to &amp; and all < to &lt; so that it can appear in HTML. The conversion also puts a line number in front of each line. Actually this macro was used to list the source of the module example.pm

The name of the file to open is presented as the argument of the macro. This is usually a relative name, i.e. relative to the input file. To locate this file the function calls the function &jamal::input which is a function defined in the jamal source to support Perl macro extensions, like this.

There is also a notable function call just before the function returns the resulting string: &jamal::verbatim. This function call tells not to process the returned string further for macros, instead take it as it is and put into the output file.


3. Macros implemented as functions

Each Perl module written macro is function in the module it is implemented. When a macro looks like something implemented in a module takes the name of the macro, and calls the function with the same name from the module. If the functin is not implemented by the module shows an error message an a null string is used as the result of the macro.

The functions implementing a macro get one argument: the string that stands between the name of the macro/function and the macro closing string in the source file.

The return value of the function should be a string, the result of the macro evaluation.

The function can access several resources supported by . The program defines several functions in the package jamal that can be used by the macro implementing functions to access resources. For the list of the functions see 6. Functions in the package jamal.


4. Version maintenance

As modules are developing they have versions. In a newer version of a module there can be a feature which is not implemented by an older version, or is implemented different. On the other hand there can be some features in a version that are not implemented in later versions.

Whenever a source file uses a module it is a good practice to declare the version the source needs. This version information is passed to the module and the module can decide wheter it is compatible with the requested version or not.

A package can tell two version numbers. These are the lowest and the highest version number that the package is compatile with. To tell these numbers the package should call &jamal::version in the BEGIN block of the module with two arguments: the low version and the high version. generates a fatal error and stops after the module is loaded if the requested version is smaller than the low version or higher than the high version.

At the same time calling the function &jamal::version not only tells the implementation version limit but also returns the version the source requested. If the implementation of the module is different for different versions, it can alter its normal behaviour according to the requested version. (See 2. An example)


5. Tuning macro evaluation order

Macro evaluation order is the same for Perl module defined macros as it is for built-in or user defined macros.

When a Perl module defined macro is used with the @ character preceeding it the argument of the macro will be passed to the macro without evaluation. If the # character is used the macro argument will be scanned for embedded macros, these macros are evaluated, and the result is passed to function implementing the macro. This is the same as with other macros.

The result of a macro usually is scanned for embedded macros to be resolved. There are a few exceptions, like the built-in macro null which purposefully does not resolve embedded macros. This is the choice of the macro itself and has nothing to do with the macro preceeding @ or # character.

Module extensions can decide wheter they request to process their output for embedded macros or not. The default behaviour is that processeses the output returned by the function implementing the macro. When a function wants to skip processing the returned output it should call the function &jamal::verbatim. This function call tells not to process the returned string further for macros, instead take it as it is and put into the output file.

Functions implementing a macro are not required to behave always the same. They can decide at run time to call or not to call the &jamal::verbatim function.

What is more they can change their mind even after calling this function before returning the result making a &jamal::verbatim(0) call.


6. Functions in the package jamal

The Perl package jamal is implemented in the source file jamal.pl. These functions are available for the Perl written extension modules to access resources, like the module version that the source file requested.

The list of these functions and their behaviour can be found in the Perl source documentation generated by esd2html.


7. Modifying the source of . NO!

There should not be any need to modify the source of on the user side. In case you feel you must modify follow the following rules:
  1. Do not modify the source , or
  2. follow rule number 1.
Sorry. But being honest: no man is perfect. You might really need some modifications. This can be because
  • you need something that can not be done using the extension even though I tried my best effort to make the interface as general as possible,
  • there is some bug that you should immediately correct,
  • you have some great idea that you think should really be core functionality of ,
In any of these case, I ask you to notify me. Notifying me has several advantages:
  • I tell you that your desired functinality can indeed be implemented using the defined interface,
  • I will correct the bug, and the next version of won't contain it,
  • I tell you that what you need is under development, you need not reinvent the wheel,
  • I might implement the functionality into next version and all users can get it.
You can also notify me before starting to write an extension. I might drop you a mail telling that there is already something on the net.

8. Submitting a module

You can send me modules to be published. I plan to maintain collection of Perl written extension modules. However I will include only modules that I like. A bit more elaborate:
  • they are under the GNU General Public License,
  • compliant,
  • worth publishing.

This page was created using jamal v3.01