PML Language Reference



Introduction

What's the Problem

Here's the problem. You develop this really cool CGI script, you spent months making the HTML that it generates look good. Then someone tells you "Let's make a few changes to the interface". Well, now you have to dig through your perl code and change all that HTML that you are generating.
Wouldn't it be nice if you could put all your HTML in seperate files! Then you would only have to change those files to update the interface, and no one whould have to chnage any code. That brings a whole new problem though, because there is some HTML that you generate on the fly and there is no way to make it static and put it in a file. In comes PML.

PML can help

PML allows you to take all your HTML from within your script and put it into files. Those files are mostly HTML, but also contain a little of the PML Markup Language to do things like variabels and flow control. Now your CGI script can have PML read a file, send it some variables and send the results to the browser. PML is simple enough so that you can have your HTML team edit the files, giving you more time to code.

Example

The best way to describe PML is to take a quick look at some.
<html>
<head>
<title>${title}</title>
</head>
<body>
	@if (${i_am_frank})
	{
		 <h1>Cool, you are Frank!</h1>
	}
	@else
	{
		 <h1>You must not be Frank.</h1>
	}
</body>
</html>

The first thing you see is ${title}. That is a PML variable. When PML is executing your PML file, it will replace the ${title} string with the actual value of that variable. That value can be set from the command line, from within your perl script, from within the PML text and also from within PML itself.
Next you will see "@if (${i_am_frank})". This should look very familiar to a programmer. When PML is executing your PML file, is will decide if the variable ${i_am_frank} is true. If it is, PML will output the text that is in the "if block". Otherwise it will output the text in the "else block".
PML offers a lot more then what we have seen so far. You can also extend PML from within your perl application or by writing a PML Module in perl.

General Syntax

Text and Whitespace

All text and whitespace is preserved. The way that you alter the output text is using PML functions and variables. PML functions begin with an '@' symbol. PML variables begin with a '$' sign. If you want to use a '@' or '$' in your text, you will need to place a backslash '\\' in front of it. This also makes it so if you need to put a backslash in your text, you will need to put in two.

Arguments

Most PML functions take arguments. The PML set function needs to know which variable to set and what value to set. Arguments are given inbetween parathensis. Each argument is seperated by a comma. There is no need to put quotations around text, unless it contains a comma. You may use variable, and even other PML functions inside the argument list. The argument list can also be empty for some PML functions.
Here are some examples of arguments, using the PML set function:
	# lines that START with a sharp/pound/hash are skipped
	# how about one argument
	@set(ThisIsTheNameOfOneVariable)

	# And now two arguments
	@set(myvariable, Set myvariable to this string)

	# or three
	@set(x, this is two, this is three)

	# An argument that is a variable
	@set(x, )

	# An argument that is a PML function
	@set(x, @perl{1})

Blocks

Some of the PML functions need blocks of text to work with. One example is the PML if function. The if function takes one argument and a block. It will output the block of text if the argument is true.
Inside of the block, you can use anything that you can use inside of a block. These include, plain text, variables and functions. A block is the text inbetween braces { and }.
Here is an example using the PML if function:
	@if( this is true) {
		Then this text will get output
	}
Because the parser uses the braces as delimiters, you must use caution that you don't use a brace unless you place a backslash before it.
Here is a block inside another block:
	@if( this is true) {
		here comes another
		@if( this one too) {
			this is block two
		}

		back to block one
	}

Comments

Comments is text that is skipped by the parser. It is a great way to document what your text is doing, or to temparily to remove text. PML comments are started by a #, as long as it is the first non-whitespace charater on the line. They last until the end of the line. To use a # you should put a backslash before it.
	# This is a comment
	This # is not
	\\# This is not either


Variables

What's a variable?

Just like in most programming languages, PML supports variables. Variables are a way to label some data, they are like containers with names. When you use a variable in PML it is replaced with what ever data it contains.
Let's look at an example:
	@set('cookiejar', 'peanutbutter')
	The cookiejar has ${cookiejar} cookies
Let me explain what just happedned. The first line put the data peanutbutter into the variable (container) cookiejar. From this point on, when ever your make mention of cookiejar PML will replace it with peanutbutter. In order to keep PML from replacing the word cookiejar when you really wanted the word cookiejar it requires you to inclose your variable name in ${} like on line two above. When ever PML sees something like this: ${word} it will look up the word and replace that text with the data in that variable.
And in case you were wondering, when you run that PML code from above this is what gets returned from PML:
	The cookiejar has peanutbutter cookies

Variable Names

All variable names must begin with either an underscore '_' or a letter. It can only contain letters, numbers and underscores.

Data

Variables can contain more then one value at a time. It is like a Perl array (internaly it is a Perl Array Reference). You can access the different 'elements' of your variable using a subscript like this:
	@set('cookiejar', 'peanutbutter', 'oatmeal', 'sugar')
	Cookie one is ${cookiejar[0]}
	Cookie two is ${cookiejar[1]}
	Cookie three is ${cookiejar[2]}
This prints
	Cookie one is peanutbutter
	Cookie two is oatmeal
	Cookie three is sugar
For a better way to do the above see @foreach. There are other @set functions too, like @append so see those sections for more information.

set

The set function sets a variable. It takes as it's arguments, a variable to set and the value(s) to set it to.
	@set(name, Peter)
	#name is now set to Peter
	${name}
If you give more then on value, the variable will be set as an array. You can then access the individual values by number, starting at 0.
	@set(cookies, peanutbuttter, peanutbutter chip, oatmeal)
	${cookies[0]}
	${cookies[1]}
	${cookies[2]}


setif

The setif function is the same as the set function except that the setif function will not set the variable if it already holds a value.

Append

The append function appends a string to a variable. Before the append, the data in the variable will have spaces removed from the end of it. Then all the spaces will be removed from the front of the string. Finally the sting is append to the variable with one space between.
If the variable is an array, append will add another element to the end of the array.
	@set(myvar, Test One)
	@append(myvar, and Two)
	# myvar is now "Test One and Two"

	@set(test, 1, 2, 3)
	@append(test, 4)
	# test is now (1, 2, 3, 4) (array)


prepend

prepend is just like append except that the string is added to the front of the variable, with a space between the string and the variable. If the variable is an array, the list is unshifted on to the array.

Concat

The concat function just like the append function except concat does not add that one space.

The if function

How to make a descesion

You can use the @if function to tell PML what text to output based on some other data. Let's take a look:
	@if(1)
	{
		This text is printed if the condition is true
	}
	@else
	{
		This text is printed if the condition is false
	}
The @if function requires that you give it a condition. If that condition is true then it will process the text in the block flowing the @if. If the condition is false and you supply an @else then PML will process the text in the block following the @else. The @else function is optional, but if you use it, it must follow an @if function.

True and false

What is true and false is the same as what Perl considers true or false. The very simple answer is: Blank conditions and 0 are false, everything else is true.

Cool stuff in the condition

Just like all other PML functions that take arguments, the @if function can take variables. Let's look at another example:
	@set('condition', 1)
	
	@if (${condition}) {
		This text will get printed
	}
	
	@set('condition', 0)
	
	@if (${condition}) {
		This text will NOT get printed
	}

elsif

Just like Perl, PML has a @elsif function. This function must follow a @if function, and like @if, it must be given a condition. If the condition to @if is false, then the condition to the next @elsif function is checked. You can have as many @elsif functions that you want. Here is an example:
	@set('c1', 0)
	@set('c2', 0)
	@set('c3', 1)
	
	@if (${c1})
	{
		This text will be printed if c1 is true
	}
	@elsif (${c2})
	{
		This text will be printed if c2 is true
	}
	@elsif (${c3})
	{
		This text will be printed if c3 is true
		(which it is in this example)
	}
	@else
	{
		This text will be printed if nothing above
		was true.
	}

unless

Another function that came from Perl is the unless function. unless is just like if except that the code in the unless block is executed when the condition is false not true. unless supports elsif and else just like if.
	@set( condition, 0 )
	
	@unless ( ${condition} ) {
		This text will get output because
		the condition is false
	} @else {
		This text would get output if the
		condition was true
	}


The foreach function

How to loop

The foreach function is used to loop over a set of values. Let's look at an example.
	@foreach ( 1, 2, 3, 4, 5, 6 ) {
		This is loop number ${.}\n
	
	}
The output of the above code is:
	This is loop number 1
	This is loop number 2
	This is loop number 3
	This is loop number 4
	This is loop number 5
	This is loop number 6
Each time through the loop, the special variable ${.} will get set to the current argument. That special \n insterts a new line. This is necessary to get the next loop to be on a new line. This makes it possible to loop and keep the loop text on the same line.

Looping over variables

You can also loop over an array you have set.
	@set( cookiejar, peanutbutter, oatmeal, sugar )
	
	@foreach ( ${cookiejar} ) {
		I like ${.} cookies\n
	}
That code would output:
	I like peanutbutter cookies
	I like oatmeal cookies
	I like sugar cookies


The include function

The PML include function is used to include another file in the place of the include function. It takes an argument list of files to include.
To place file 'b' inside file 'a' at runtime use the following code:
	# This is file a
	Blah Blah Blah
	@include(b)
The include function is special in that it is a parse time function. This means that the file you are including must exist during the parsing of the original file, and you can not use variables or other functions in the argument list of the include. One exception to this rule is if the variable was defined outside the file, such as in a Perl script.

PML Macros

What is a macro?

A macro is a way to repeat text over and over again without having to type it over and over again. It lets you group some text together and give it a name. Then to use that text you just call it by name.

Defining a macro

Use the PML macro function to define a macro. The macro function takes arguments and a block. The first argument is the name to call the macro and the block is the actual text. Example:
	@macro( mymacro ) {
		This text is called mymacro
	}

Using the macro

To insert the text from above macro use call it by it's name:
	Some text before the call to the macro
	@mymacro() 
	Some text after the call to the macro
After that text is processed, it will look like:
	Some text before the call to the macro
	This text is called mymacro
	Some text after the call to the macro

Macro arguments

PML macros can take arguments. Here is an example:
	@macro( mymacro, myvariable ) {
		The variable you passed was ${myvariable}
	}
Then to call that macro you just:
	@mymacro(test)

The ARGV Variable

If your macro was given more arguments then you declared in your macro definition, they will be avaliable in the ${ARGV} varible which is an array.

Need

The need function is used to load in a external PML Module. It takes a list of PML Modules to load:
	@need(CGI)
	@need(LWP)
Note: these are PML modules, not Perl Modules

The perl Function

The perl function is used to eval perl code. This function does not take any arguments, just a block. The code to eval should be in the block.
You can access the PML variables inside the evaled perl by the hash %v.
	@set(x, 1)
	@perl{ $v{x}++; undef}
The reason that you need the undef is because the output from the eval is injected into the output stream. Your Perl code will always be evaled in list context.
	@perl{ scalar localtime }
	# puts the localtime into the output stream


Replace If Blank (rib)

This PML function is very useful for HTML tables. It is like a reversed if function. It takes both an argument and a block. If the block turns out to be empty, then the function will return the string that was passed in the argument list. Here is an example used inside HTML:
<td>
	@rib( &nbsp; ) {
		@if (condition) {
			text
		}
	}
</td>
If the if function from above does not return any text, the rib function will place a &nbsp; in the output stream.

warning

The warning function controls weather or not PML will produce warning messages. By default, warnings are turned off. Setting the warning flag to a true value will turn them on.
	@warning(1)
	${new}
	# this will produce a warning because
	# new did not have a value
	@perl{ldkj';;}
	# warning above because the perl statement
	# had syntax errors


The while function

while loops

The while function lets you repeat some text over and over while some condition is true.
	@set(condition, 1)
	@set(i, 1)
	@while(${condition}) {
		Loop number 
	}
That code produces:
	Loop number 1
	Loop number 2
	Loop number 3

until loops

You can use until instead of while. The only difference is that until only loops while the condition is false.

The wrap Function

wrap will take some options as it's arguments and alter the text inside it's block. Here are the arguments to give to wrap
  1. Number of columns to wrap to (Defaults to 80)
  2. Charater to use before wrap (Defaults to blank)
  3. Charater to used to start wraped lines (Defaults to blank)