Perl MtmlParser Documentation

Description

The Parser Perl module is a package for parsing text. When used to make CGI applications, the programmer can specify the final HTML pages via MTML templates. An MTML template is simply ascii text that contains variables and callbacks. Representing pages using these templates allows for changes to be made to the pages without having to change any code.

Basic Usage

To use Parser.pm in a Perl program, one should follow these steps:

  1. create a new instance of the parser using the new method.
  2. add any name - value variable pairs and/or custom callbacks desired.
  3. use the object to parse a piece of text. This text may be obtained from a scalar text string or from a file, via ParseText or ParseFile functions, respectively.

The following sample code parses a file and prints the result to STDOUT:

#!/usr/local/bin/perl

sub BEGIN {
        #lib is the directory where MtmlParser.pm lives.
	$lib = "/home/httpd/html/lib";
	push (@INC, $lib);
}

my $file = shift @ARGV;
my $p = new MtmlParser();
my $text = $p->ParseFile($file);
print $text;

Any text file can be put through the parser, but as a standard, specify a file holding text to be parsed with the parser with a .mtml extension.

Public Interface

These are the functions that make up the public interface.

Descriptions:

AddMtmlCallback(callback_name, package_name_or_reference)

Adds a callback to the parser's list of callbacks.

callback_name
Callback to be added to the callback table.

package_name_or_reference optional
Either the name of the package where the callback resides or a reference to the package. If the second parameter is not provided, the main package will be used.

Returns: 1 if callback was successfully added to table.

Usage:

$p->AddMtmlCallback("MyCallback", $self);


AddMtmlCallbacks(callback_names, package_name_or_reference)

Adds a list of callbacks to the parser's list of callbacks.

callback_names
Callbacks to be added to the callback table. This parameter is a reference to an array of names to be added.

package_name_or_reference optional
Either the name of the package where the callback resides or a reference to the package. If the second parameter is not provided, the main package will be used.

Returns: 1 if callbacks were successfully added to table.

Usage:

$p->AddMtmlCallbacks(["Callback1", "Callback2"], $self);


SetVar(var_name, var_value)

Sets a variable to a value.

var_name
Name of variable to be set.

var_value
Value of variable to be set.

Returns: 1 if variable was successfully set.

Usage:

$p->SetVar("name", "Mike");


DeleteVar(var_name)

var_name
Variable to be deleted from the variable table.

Returns: 1 if variable is successfully deleted from table.

Usage:

$p->DeleteVar("name");


DumpVars()

Used for debugging.

Returns: all name - value pairs in the variable table.

Usage:

$p->DumpVars();


GetValue(var_name)

var_name
Variable to be searched for in the variable table.

Returns: value of variable, if found in table.

Usage:

$p->GetValue("name");


IsMtmlCallback(callback_name)

callback_name
Callback to search for in list of callbacks.

Returns: 1 if callback exists in list.


new()

Returns: a new Parser object.

Usage:

$p = new MtmlParser();


ParseText(text_string)

text_string
Text string to be parsed.

Returns: parsed text.

Usage:

$text = "Hello my name is ${name}";
$p = new MtmlParser();
$p->SetVar("name", "Mike");
$parsed_text = $p->ParseText($text);


ParseFile(file_name)

file_name
File containing text to be parsed.

Returns: parsed text.

Usage:

$p = new MtmlParser();
$p->SetVar("name", "Mike");
$parsed_text = $p->ParseFile("./my_template.mtml");


Reset()

Clears all of the variables from the variable table.

Returns: 1 if table is cleared.

Usage:

$p->Reset();


VarExists(var_name)

var_name
Variable to search for in the variable table.

Returns: 1 if variable exists in table.

Usage:

if ($p->VarExists("name")) {
  ....
}
else {
  $p->SetVar("name", "Some Default Name");
}


Variables

Variables have the syntax: ${var_name}, where var_name is the name of the variable. In the template one can set a variable as follows:

${var_name} = value

For example:

${foo}="This is a variable"

In this example, the value for the variable is enclosed in double quotes, but any delimiter may be used as long as the closing delimiter matches the opening delimiter. The value may span more than one line. So, a template such as:

${title}=|This is my page|
${bg_color}="white"
${name}='Mike'
<HTML>
<HEAD>
<TITLE>${title}</TITLE>
<HEAD>

<BODY BGCOLOR=${bg_color}>

<H1>Hello ${name}</H1>

</BODY>
</HTML>

could be parsed to expand its variables and produce the following:

<HTML>
<HEAD>
<TITLE>This is my page</TITLE>
<HEAD>

<BODY BGCOLOR=white>

<H1>Hello Mike</H1>

</BODY>
</HTML>

Note: to prevent a variable from being expanded, precede it with a backslash:

\${var_name}

Callbacks

A callback executes a subroutine of the same name in the Perl code and is expanded to whatever text is returned. There are a number of callbacks that are built in to the main parser. Additionally, one can define new callbacks. This allows a piece of code to be embedded into a template without putting the actual code in the template. By making flexible callbacks whose output depends on the input parameters, it is posible to make highly reusable callbacks.

The syntax for a callback is:

@nameOfCallback(%`var1`, `var2`, ... %)

Notice that a callback can have a variable number of parameters passed to it. Each parameter is enclosed in back-ticks (`), not single quotes ('). These parameters will be passed to the subroutine that is invoked by the callback.

As with variables, a callback may be preceded with a backslash if it should not be expanded:

\@nameOfCallback(%`var1`, `var2` ... %)

The pre-defined callbacks are:

And here are the details:

@LoadConfig(%`filename`%)

filename
Name of a config file that adds variables to the variable table.

A configuration file is a setup file that assigns values to variable names. One might contain text like:

name = "mike"
fav_color = "blue"
bg_color = "white"

Returns: 1 if successful.

Usage:

@LoadConfig(%`/home/me/setup/mysetup.setup`%)
@LoadConfig(%`${setup_dir}/mysetup.setup`%)


@Include(%`filename`%)

filename
Name of a template file to be included. This works like an SSI except that the included template is also parsed and therefore may contain MTML text.

Returns: 1 if successful.

Usage:

@Include(%`/home/me/templates/head.mtml`%)
@Include(%`${template_dir}/head.mtml`%)


@IncludeHtml(%`URL`, `flag`%)

URL
Name of URL, the contents of which will be included. This works like an SSI except that the included URL may also be parsed and therefore may contain includes.

flag optional
Switch, when set to true (1), indicates that the URL's contents should be parsed.

Returns: true if successful.

Usage:

@IncludeHtml(%`http://www.splunge.com/cgi-bin/something.cgi`, `1`%)
@IncludeHtml(%`www.whitefire.com/index.html`%)


@Exec(%`command`%)

command
shell command to be executed like a server side #exec, (using /bin/sh).

Returns: results of command execution.

Usage:

@Exec(%`ls -l | grep *.html`%)
@Exec(%`/home/me/bin/maketext.pl`%)


@If(%`condition`, `string1`, `string2`%)

condition
Boolean expression. Supported operators are:

string1
String to be returned if condition is true.

string2 (optional)
String to be returned if condition is false.

Returns: string1 if condition is true. otherwise returns string2 if given else an empty string.

Usage:

>@If(%`${myVar} > 2`, `My variable is greater than 2!`%)
@If(%`${name} eq "Mike"`, `Hello Mike`%)
@If(%`${name} eq "Mike"`, `Hello Mike`, `You are not Mike!`%)


@Unless(%`condition`, `string`%)

condition
Boolean expression. Supported operators are:

string
String to be returned if condition is false.

Returns: string if condition is false or an empty string is condition is true.

Usage:

@Unless(%`${name} eq "Mike"`, `You are not Mike!`%)


@IfDef(%`var_name`, `string1`, `string2`%)

var_name
Name of variable whose existence is checked in the variable table.

string1
String to be returned if var_name exists.

string2 (optional)
String to be returned if var_name does not exist.

Returns: string1 if var_name exists in the variable table. otherwise returns string2 if given else an empty string.

Usage:

@IfDef(%`name`, `Hello ${name}!`%)
@IfDef(%`name`, `Hello ${name}!`, `who are you?`%)


@IfNotDef(%`var_name`, `string1`, `string2`%)

var_name
Name of variable whose existence is checked in the variable table.

string1
String to be returned if var_name does not exist.

string2 (optional)
String to be returned if var_name exists.

Returns: string1 if var_name does not exist in the variable table. otherwise returns string2 if given else an empty string.

Usage:

@IfNotDef(%`name`, `No name has been given`%)
@IfNotDef(%`name`, `No name has been given`, `name is ${name}`%)


@IfEmpty(%`var_name`, `string1`, `string2`%)

var_name
Name of variable whose existence / contents are checked in the variable table.

string1
String to be returned if var_name does not exist or is an empty string.

string2 (optional)
String to be returned if var_name is not an empty string.

Returns: string1 if var_name is either an empty string or is not defined in the variable table. Otherwise, returns string2 if given else an empty string.

Usage:

@IfEmpty(%`name`, `You must give me your name!`%)
@IfEmpty(%`name`, `You must give me your name!`, `hello {$name}`%)


@IfNotEmpty(%`var_name`, `string1`, `string2`%)

var_name
Name of variable whose existence / contents are checked in the variable table.

string1
String to be returned if var_name is not an empty string.

string2 (optional)
String to be returned if var_name does not exist or is an empty string.

Returns: string1 if var_name is defined in the variable table and is not an empty string. Otherwise, returns string2 if given else an empty string.

Usage:

@IfNotEmpty(%`name`, `Hello ${name}!`%)
@IfNotEmpty(%`name` `Hello ${name}!`, `what is your name?`,%)


Defining New Callbacks

To define a new callback, the programmer needs to:

  1. write method X in an arbitrary package / namespace.
  2. add the name of method X to the new method by using the AddMtmlCallbacks function in the new constructor.

Consider the following example, in which a callback is created for the GetRandomNumber method.

The original method is:

sub GetRandomNumber {
   my $self = shift;
   my $num_digits = shift;
   my $beg_wrapper = shift;
   my $end_wrapper = shift;
   my $number = rand($num_digits);
   my $text = "$beg_wrapper $number $end_wrapper";
   return $text;
}

The new method is:

sub new {
    my ($pkg, $r_args) = @_;
    my $self = $pkg->allocate($r_args);

    #Uncomment below and replace names in array with names of
    #your callback routines
    $self->AddMtmlCallbacks(["GetRandomNumber"]);

    return $self;
}

The MTML text that uses this callback would be:

My random number is @GetRandomNumber(%`4`, `<font size=+2><B>`, `</b></font>`%)
<P>

This might output HTML such as:

My random number is <font size=+2><B>3253</b></font>
<P>

Which looks like:

My random number is 3253

Uses for Parser

One of the best uses for this parser is for making multilevel templates for a group of HTML pages. The first step towards this end is determining which elements are common to all of the pages.

For example, consider a small web site with three sections:

The HTML for each of the three pages are:

Home.html:
<HTML>
<HEAD>
<TITLE>Home</TITLE>
<HEAD>

<BODY BGCOLOR=white>

<H1>Welcome</H1>

This is my home page. Look around but wipe your feet first.
<br>

<hr>
Home<br>
<A HREF="Links.html">Links</A><br>
<A HREF="About.html">About</A><br>

</BODY>
</HTML>

Links.html:
<HTML>
<HEAD>
<TITLE>Links</TITLE>
<HEAD>

<BODY BGCOLOR=white>

<H1>My favorite links</H1>

<A HREF="http://www.splunge.com">Splunge</A><br>
<A HREF="http://www.foo.com">Foo</A><br>
<A HREF="http://www.bar.com">Bar</A><br>

<br>

<hr>
<A HREF="Home.html">Home</A><br>
Links<br>
<A HREF="About.html">About</A><br>

</BODY>
</HTML>

About.html:
<HTML>
<HEAD>
<TITLE>About</TITLE>
<HEAD>

<BODY BGCOLOR=white>

<H1>About this site</H1>

The webmaster of this site is a no good dirty swine. Send all email
to <A HREF="mailto:webmaster@swine.com">webmaster@swine.com</A>
<br>

<hr>
<A HREF="Home.html">Home</A><br>
<A HREF="Links.html">Links</A><br>
About<br>

</BODY>
</HTML>

These three pages have some common elements. Specifically, the header, navigation, and footer HTML are similar, with minor differences. Most cases are more elaborate -- containing more in-between text -- but this is adequate for demonstration.

The most common things that would be extracted out for HTML page templates are, in fact, the header and the navigation. The text for each page is not exactly the same though; the title and navigation differ slightly. So, what would the appropriate templates look like? Like these:

Header.mtml:
<HTML>
<HEAD>
<TITLE>${title}</TITLE>
<HEAD>

<BODY BGCOLOR=white>

Footer.mtml:
<hr>
@IfNotDef(%`NO_SHOW_HOME`, `<A HREF="Home.html">Home</A><br>`, `Home`%)
@IfNotDef(%`NO_SHOW_LINKS`, `<A HREF="Links.html">Links</A><br>`, `Links`%)
@IfNotDef(%`NO_SHOW_ABOUT`, `<A HREF="About.html">About</A><br>`, `About`%)

</BODY>
</HTML>

The previous two templates are then incorporated into the following templates:

Home.mtml:
<!-- set ${title} for this template: -->
${title}="Home"
<!-- notice that $NO_SHOW_HOME _is_ defined: -->
$NO_SHOW_HOME="yup"
@Include(%`Header.mtml`%)

<H1>Welcome</H1>

This is my home page. Look around but wipe your feet first.
<br>

@Include(%`Footer.mtml`%)

Links.mtml:
${title}="Links"
$NO_SHOW_LINKS="yup"
@Include(%`Header.mtml`%)

<H1>My favorite Links</H1>

<A HREF="http://www.splunge.com">Splunge</A><br>
<A HREF="http://www.foo.com">Foo</A><br>
<A HREF="http://www.bar.com">Bar</A><br>

<br>

@Include(%`Footer.mtml`%)

About.mtml:
${title}="About"
$NO_SHOW_ABOUT="yup"
@Include(%`Header.mtml`%)

<H1>About this site</H1>

 The webmaster of this site is a no good dirty swine. Send all email
to <A HREF="mailto:webmaster@swine.com">webmaster@swine.com</A>
<br>

@Include(%`Footer.mtml`%)

The files Home.mtml, Links.mtml, and About.mtml are the templates for the three main pages. Each one sets two variables: ${title}, and a switch variable ($NO_SHOW_ABOUT, $NO_SHOW_LINKS, or $NO_SHOW_HOME) that ensures that the current page does not contain a 'hot' link to itself.

You may notice that each of the main templates has a very similiar structure. This leads to a second way of constructing the templates that actually makes them easier to maintain. Instead of having three pages that each include a header and a footer with text between the two includes, the overall structure can be represented in one general template with the text specific to each page put into a seperate template. So now our templates woule look like:

main.mtml:
<HTML>
<HEAD>
<TITLE>${title}</TITLE>
<HEAD>

<BODY BGCOLOR=white>

@Include(%`${sub_template}`%)

<hr>
@IfNotDef(%`NO_SHOW_HOME`, `<A HREF="Home.html">Home</A><br>`, `Home`%)
@IfNotDef(%`NO_SHOW_LINKS`, `<A HREF="Links.html">Links</A><br>`, `Links`%)
@IfNotDef(%`NO_SHOW_ABOUT`, `<A HREF="About.html">About</A><br>`, `About`%)

</BODY>
</HTML>

Home.mtml:

$NO_SHOW_HOME="yup"

<H1>Welcome</H1>

This is my home page. Look around but wipe your feet first.
<br>

  

Links.mtml:

$NO_SHOW_LINKS="yup"

<H1>My favorite Links</H1>

<A HREF="http://www.splunge.com">Splunge</A><br>
<A HREF="http://www.foo.com">Foo</A><br>
<A HREF="http://www.bar.com">Bar</A><br>

<br>

  

About.mtml:

$NO_SHOW_ABOUT="yup"

<H1>About this site</H1>

 The webmaster of this site is a no good dirty swine. Send all email
to <A HREF="mailto:webmaster@foobar.com">webmaster@foobar.com</A>
<br>

  

With this version you will need to fill in the ${title} and ${sub_template} variables as appropriate according to wich page you wish to see before you parse main.mtml. Thus to show the about page your code might look like:

$p = new MtmlParser();
$p->SetVar("title", "About");
$p->SetVar("sub_template", "About.mtml");
$text = $p->ParseFile("Main.mtml");
print $text;

Although it may seem a bit backwards at first, this kind of templating scheme makes it easy to maintain your templates.

As noted before, real templates would be more complicated. One can set variables in the parser in of the program before the template is parsed (either manually or through a setup file). Then the templates themselves can load setup files to set variables (via the LoadConfig callback).

As a general design principle, when using the Parser to make CGI applications, it is best to keep the templates as simple as possible and put the programming logic in the code. The point of a template is for it to be easily modified. Before the template is expanded, one's code should go through whatever programming logic is needed to populate the variables that will be embedded in the template. If there is a particular piece of logic that is being used repeatedly, that logic can be encapsulated into a new callback.