Even without making use of moto's programmatic constructs, the moto preprocessor can
do away with much of the repetition involved in hand coding pages of HTML. It provides
functionality for text file inclusion and macro substitution.
Just as in the C preprocessor the include directive is used to copy the contents of
another file into a moto page at the point in the page where the directive is used.
This directive may be used as a replacement for server side includes
to include image maps or common web page navigation. Moreover, since file inclusion occurs in the
preprocessor phase, included files can contain moto source code. The path to the include is relative to the including page or, if the path begins with a /, is relative to the file system root.
The moto preprocessor also has the capability to do macro expansion within moto pages. Macros are used
like moto constructs in that they also begin with a dollar sign. This document uses macros to format
each of it's examples. The example above was formatted by the following code:
$BEGIN_USAGE
#include(path to include)
$END_USAGE
| |
Macros may require arguments. These arguments need not (and often should not) be quoted as they will be
substituted without modification where they are used in the macro definition. Argument lists are comma
delimited thus if one argument contains a comma (or a close paren ')') it must be escaped with
a backslash '\'. An example of a macro that takes arguments used in the construction of this document is
the macro used for specifying section headers:
$BEGIN_SECTION("Macro Substitution")
| |
The #readdefs preprocessor directive makes the macros contained in a text file (called a macro library)
available for substitution in the preprocessing phase.
#readdefs(path to macro library file)
| |
The path to the macro library can be relative to the page containing the readdefs directive or fully qualified from your root directory. Macro libraries have names ending with '.lib'. The rules for constructing a library file are
as follows:
- Lines beginning with a pound sign '#' are ignored. These are comments in the library file.
- Lines beginning with text (without indentation) begin a macro definition. The first word
on that line is the macro name.
- If the macro is to take arguments, the macro name should be immediately followed by
an open paren '(', then a comma delimited list of argument names,
and finally a close paren ')'.
- Lines beginning with white space are considered part of the current macro definition thus
a macro definition ends when the file ends or the next macro definition begins.
- Macros may be used in the definitions of other macros but self referential macro calls
are prohibited.
- A macro argument is used within the body of a macro as if it were another macro i.e. they are called
by $macro argument name
The macro library used in the creation of this document looks like:
#
# Called at the beginning of a document, this macro sets up the variables
# needed to create and store the outline of that document
#
INIT_FORMAT
$use("codex.util")
$declare(int levelCounter)
$declare(int chapNum = 0)
$declare(Stack outline = new Stack())
$declare(Vector curLevel = new Vector())
$do(outline.push(curLevel))
#
# This macro should be called at the beginning of a section or
# or subsection with the header for that section or subsection. It
# will add the section header to the outline and compute and display
# the appropriate section / subsection number. It will also insert an HTML
# anchor tag so that this section may be linked to from the outline
# (autmatically generated by the DISPLAY_OUTLINE macro)
#
BEGIN_SECTION(section)
$do(curLevel = <Vector>outline.peek())
$do(curLevel.add($section))
$do(curLevel.add(new Vector()))
<h3>
$for(levelCounter=outline.size();levelCounter>1;levelCounter--)
$((<Vector>(outline.peekAt(levelCounter))).size()/2 +
(levelCounter==outline.size()?chapNum:0)).
$endfor
$(curLevel.size()/2 + (levelCounter==outline.size()?chapNum:0))
<a name="$($section)">$($section)</a>
</h3>
$do(outline.push(curLevel.get(curLevel.size()-1)))
#
# This macro should be called at the end of a section or subsection
#
END_SECTION $do(outline.pop())
#
# This macro is used to generate the HTML for a hyperlinked outline of
# a document marked up with the preceding macros
#
GENERATE_OUTLINE(outputFile)
$declare(int curIndex = 0)
$declare(IntStack istck = new IntStack())
$declare(Stack ostck = new Stack())
$declare(StringBuffer obuffer = new StringBuffer())
$do(ostck.push(outline.peek()))
$do(istck.push(0))
$while(ostck.size() > 0 )
$do(curLevel = <Vector>ostck.peek())
$do(curIndex = istck.peek())
$if(curIndex == curLevel.size() )
$do(curLevel = <Vector>ostck.pop())
$do(curIndex = istck.pop())
$if(istck.size()>0)
$do(istck.push(istck.pop()+2))
$endif
$continue
$endif
$for(levelCounter=istck.size();levelCounter>0;levelCounter--)
$do(obuffer.append(istck.peekAt(levelCounter)/2 + 1))
$do(obuffer.append('.'))
$endfor
$do(obuffer.append("<a href=\"docs"))
$do(obuffer.append(istck.peekAt(istck.size())/2 + 1))
$do(obuffer.append(".moto#"))
$do(obuffer.append(<String>curLevel.get(curIndex)))
$do(obuffer.append("\" onClick=\"return targetopener(this)\">"))
$do(obuffer.append("<nobr>"))
$do(obuffer.append(<String>curLevel.get(curIndex)))
$do(obuffer.append("</nobr>"))
$do(obuffer.append("</a><br>\n"))
$do(istck.push(0))
$do(ostck.push(curLevel.get(curIndex+1)))
$endwhile
$do(putFile($outputFile,obuffer.toString()))
| |
Moto is an embedded language. That means that any text file, HTML page, or XML document is
by default a legal moto file. The contents of such a file would pass through the moto
interpreter and print out unmodified. Moto actually does something when, in the process
of parsing a file, it runs into a moto construct. These are special directives that begin with
the character $.
To comment out a block of text you want moto to ignore,
surround that text by $* and *$ . Unlike HTML comments, commented text
will not appear in the output of either the moto compiler or interpreter.
To use the dollar sign $ in your moto file without what follows being interpreted as
a macro or malformed moto construct you must escape it with a backslash
Note that backslashes not followed by a dollar sign are treated as literal and need not
be escaped. Thus the code used to produce the previous example was
Moto supports the use of local variables. Prior to use, a local variable must first
be declared.
$declare(variable type variable name)
| |
The initial value of a variable may also be set at declaration time.
$declare(variable type variable name = initial value )
| |
You may not declare a variable with the same name more than once in a page. That means that you
may not declare a variable with the same name in a macro that is used on the page or in a file that
is included in the page. Unlike C, moto does not require all declarations to be at the beginning
of a page , but it's still good practice.
The storage for local variables is
lost after executing a page. Thus these variables are said to have
a page lifetime. Variables declared within a loop or conditional block
may not be used after (outside of) that conditional block or loop.
Such variable have a scope lifetime.
Variables may be of basic types or object types. Basic types include:
double |
may be assigned any byte, char, int, float, long or double value |
float |
may be assigned any byte, char, int, float, long or double value |
long |
may be assigned any byte, char, int, float, long or double value.
Moto longs are 64 bits in length thus the maximum and minimum values are 9223372036854775807 and
-9223372036854775808 respectively |
int |
may be assigned any byte, char, int, float, long or double value.
Moto ints are 32 bits thus the maximum and minimum values are 2147483647 and -2147483648
respectively |
byte |
may be assigned any byte, char, int, float, long or double value. Bytes are signed
ints 8 bits long thus the maximum and minimum values are 127 and -128 respectively |
boolean |
may be assigned true or false or the value of a relational
expression |
char |
may be assigned any character in sigle quotes '' including standard C
escape characters such as '\n' '\r' or '\t'. It may also be assigned a
value in hex or octal representation e.g. '\x41' == '\101' == 'A' as well
as any numeric value |
| |
The char, byte, double, float, long, and int types collectively make up the supported basic numerical
types. They can be combined to form expressions via the following operations:
A + B |
adds A and B together or converts one variable to a String and concatenates
it to the other if the other is already a String. |
A - B |
subtracts B from A |
A * B |
multiplies A and B |
A / B |
divides A by B. When B is 0 a division by zero exception will be thrown |
A % B |
provides A mod B (only works if A and B are longs or ints |
| |
When numeric variables of different types appear in an expression, expanding casts are used
so that the most precise answer is maintained (i.e. a long plus an int yields a long, an int
divided by a float yields a float etc...). Only numeric variables and chars are implicitly cast,
booleans are not. The set of boolean operators will be described in
the section on conditionals.
The integral types byte, char, int, and long may also be acted on by bit manipulation
operators
A | B |
Bitwise OR will turn any bit to 1 in the expression result that was 1 in
either A or B. The corresponding bit in the result will be 0 otherwise |
A & B |
Bitwise AND will turn any bit to 1 in the expression result that was 1 in
both A and B. The corresponding bit in the result will be 0 otherwise |
A ^ B |
Bitwise XOR will turn any bit to 1 in the expression result that was 1 in
either A and B but not both. The corresponding bit in the result will be
0 otherwise |
~A |
Bitwise NOT will turn any bit to 1 in the expression result that was 0 in
A. Otherwise the corresponding bit in the result will be 0 |
A << B |
Left shift shifts the bits of A B bits to the left and fills the rightmost B
bits with 0. The A << 2 would multiply A by 4 |
A >> B |
Right shift shifts the bits of A B bits to the right and fills the leftmost B
bits with 0. The A >> 2 would divide A by 4 |
| |
Object types are anything else. As a rule, all basic types begin with a lower case
letter all object types begin with an uppercase letter. There is language level support
for three types of objects :
String |
may be assigned a character string between double quotes "" |
Regex |
may be assigned a regular expression between forward slashes /.../ |
Object |
may be assigned an object of any typed |
| |
The plus operator may also be used to append Strings together. When basic types are added to
strings they are automatically converted to strings first.
Other types of objects become available via the $use keyword.
When creating a new object use the new keyword and then call that objects constructor
$declare(Hashtable htab = new Hashtable())
| |
Objects you create may need to be deleted later depending upon which memory manager you are using.
This is done using the delete keyword.
All object types may also be assigned the special value null
$declare(Hashtable htab = null)
| |
The capital 'O' Object type is special in that it may be assigned a value of any other
object type.
$declare(Object htab = new Hashtable())
| |
This is important for objects that are designed to store other objects such
as Vectors or Stacks. There are no methods that can be called on a pure Object type.
Rather, before an Object can be used it must be cast to the appropriate type.
$declare(Object obj = new Hashtable())
$declare(Hashtable htab = <Hashtable>obj)
| |
To call a function or assign a value to a previously declared variable
use the $do construct. The following operators assign values to variables:
A = B |
The variable A is assigned the value of expression B |
A += B |
If A and B are both numeric types the variable A is incremented by the
result of expression B. If A is a String then the result of evaluating
expression B is converted to a String and appended to A. |
A -= B |
The variable A is decremented by the result of expression B (note that A
and B must both be numeric types) |
A *= B |
The variable A is multiplied by the result of expression B (note that A
and B must both be numeric types) |
A /= B |
The variable A is divided by the result of expression B (note that A
and B must both be numeric types) |
A++ |
The integer, long, byte, or char variable A is incremented by 1 |
A-- |
The integer, long, byte, or char variable A is decremented by 1 |
++A |
The integer, long, byte, or char variable A is incremented by 1 |
--A |
The integer, long, byte, or char variable A is decremented by 1 |
| |
There are times when you might want to do 'more than one thing at once' . For this
you can use the comma operator. The comma operator allows you to execute multiple expressions
one after another as a single expression. The result and type of the compound
expression is determined solely by the last expression executed:
Read 'dollar print', this construct is used to output an expression or result
of a function call. All non String expression results are converted to Strings prior
to being output.
$if(condition)
code to be executed if condition is true
$elseif(some other condition)
code to be executed if other condition is true
$else
code to be executed if previous conditions are false
$endif
| |
As in other languages, the basic conditional statement consists of an opening if
construct, followed by any number of else if conditions, followed by an optional
else construct, then a mandatory endif. Unlike languages such as C however, conditions
in moto must be booleans. The following comparison operators return boolean
values:
A > B |
numeric variable A is greater than numeric variable B |
A >= B |
numeric variable A is greater than or equal to numeric
variable B |
A < B |
numeric variable A is less than numeric variable B |
A <= B |
numeric variable A is less than or equal to numeric variable B |
A == B |
the value of basic variable A equals the value of basic variable B OR
the reference variable A refers to the same object as the reference
variable B |
A != B |
the value of basic variable A does not equal the value of basic variable B
OR
the reference variable A does not refer to the same object as the reference
variable B |
A eq B |
the String variable A has the same content as the String variable B |
A ne B |
the String variable A does not have the same content as the String variable B |
A lt B |
The String A would come before B when sorted alphabetically |
A gt B |
The String A would come after B when sorted alphabetically |
A lte B |
The String A would come before B when sorted alphabetically OR A has the same
content as B |
A gte B |
The String A would come after B when sorted alphabetically OR A has the same
content as B |
A =~ B |
the regular expression B matches a substring of the String A |
A !~ B |
the regular expression B does not match any substring of the String A |
| |
In addition the following operations can be used on boolean expressions to
create more complex boolean typed expressions.
A && B |
Expression is true if and only if subexpressions A and B are true |
A || B |
Expression is true if either subexpression A or B is true |
!A |
Expression is true if subexpression A is false |
| |
Like C, short circuit evaluation is used when evaluating compound boolean operators.
$switch(expression)
code for default case
$case(value 1)
code for case 1
$case(value 2)
code for case 2
$case(value 3)
code for case 3
$endswitch
| |
You can switch on any basic type expression as well as Strings.
Unlike C, execution does not 'fall through' cases, rather the
next case ends the previous. Another important difference from C
is the lack of an
explicitly specified default case. In moto, the default behavior
is to execute whatever code is specified between the switch statement
and the first case. The following example illustrates how switch
statements are used to display the examples used in this document.
$use("codex.util")
$declare(String example = getFile("/content/moto/parse_example.moto"))
$declare(StringBuffer displayExample = new StringBuffer())
$declare(int i=0)
$declare(char lastchar = '\0')
$for(i=0;i<example.length();i++)
$switch(example.charAt(i))
$* default *$
$do(displayExample.append(example.charAt(i)))
$case('<')
$do(displayExample.append("<"))
$case('>')
$do(displayExample.append(">"))
$case('&')
$do(displayExample.append("&"))
$case('\n')
$do(displayExample.append("<br>\n"))
$case('\t')
$do(displayExample.append(" "))
$case(' ')
$if(lastchar == ' ')
$do(displayExample.append(" "))
$else
$do(displayExample.append(' '))
$endif
$endswitch
$do(lastchar = example.charAt(i))
$endfor
$(displayExample.toString())
| |
Not a construct, but an operator, the ternary operator may be used to within an
expression to return one of two same typed outputs. In the example above the expression
returns the subexpression A if the specified condition evaluates true, B otherwise.
For loops in moto work very similar to for loops in C, C++, or Java.
$for(initialization;condition;increment)
code to be repeated
$endfor
| |
The initialization expression is executed prior to looping. It is
used to set up the initial conditions for the loop itself. The condition
expression is tested on each iteration of the loop (including the first iteration
immediately after initialization). Unline C or C++ the condition expression must be
a boolean expression. The increment expression is executed after each iteration of the
loop. It is immediately after the increment expression is executed that the condtion
is tested again. When the condition expression becomes false the loop terminates. For instance to
execute a loop ten times you might write
$for(i=0;i<10;i++)
code to be repeated
$endfor
| |
This assumes the variable 'i' was declared prior to being used in the loop. Like Java
a variable may be declared within the initialization expression.
$for(int i=0;i<10;i++)
code to be repeated
$endfor
| |
$while(condition)
code to be repeated
$endwhile
| |
Executes the code to be repeated while the boolean condition remains true.
$while(condition)
code to be repeated
$if(some other condition)
$break
$endif
more code to be repeated
$endwhile
| |
Breaks out of the last open loops (continues after the next $endfor or
$endwhile)
$while(1st condition)
code to be repeated
$if(2nd condition)
$continue
$endif
code to be repeated only if 2nd condition is false
$endwhile
| |
Returns to the first line of the most recently opened loop (and increments
the counter in a for loop).
To define a function in moto use the $define construct. In it you must specify the return type
of the function (use the return type void if the function returns nothing). Follow the
return type with the name of the function and then the arguments the function will take in
parenthesis. The argument list should take the form type argument-name,type argument-name, ...
for as many arguments as you have. You can specify an empty argument list.
$define(return-type name(argument list))
...
$return(expression)
...
$enddef
| |
Function definition in moto must occur in the top scope. You cannot define a new function inside
a condition, iterative, or scoping block. You can however use functions before they are
defined within a moto page. Unlike C there is no need (and no way) to explicitly prototype functions
before using them.
To return a value from a function use the $return construct. The type of the expression returned must however
match the specified type of the function. $return must be inside a $define ... $enddef scope or else it is
a syntax error.
You can specify multiple functions with the same name as long as they take a different number of arguments, or
different argument types, or the same argument types in a different order.
Functions in moto do not normally have access to variables declared outside of them. Only variables declared as
global before the function definition can be used within a moto function. Otherwise only arguments to that function
and variables declared within the body of the function can be used. To declare a variable as global use the global
type qualifier
$declare(global int statements=0)
| |
Like functions, globals can only be declared at the top level and are not allowed in nested scopes.
A Class is used to aggregate data of different types into a new type and associate with that
new type
methods which act on the aggregated data. Once a class is defined,
variables can be declared of type Class Name. These variables can then be
assigned instances of the Class also called Objects.
Class definition must occur in the top scope. You cannot define a new Class inside
a condition, iterative, function definition, or scoping block.
A Class is defined with the following syntax:
$class(<class name>)
<member variable declarations>
<method definitions>
$endclass
| |
Variable declarations inside a Class definition become member variables of that Class.
Functions defined within Class Definitions become methods of the Class. Methods with the
same name as the Class are called constructors. Constructors must not specify a return type.
Assignments to
member variables at declaration time become part of the Class's Implicit Constructor.
These assignments will take place whenever an instance of the Class is created. Class instances
are created by using the 'new' keyword.
$declare(<class name> <var name> = new <class name>(<constructor arguments>))
| |
Three distinct operations occur when an Object is instantiated:
- Storage is allocated for the Object
- The implicit constructor is called assigning initial values to the Object's member variables
- The explicit constructor (the constructor whose arguments match those passed in) is called
to do further initialization of member variables.
If no no-arg constructor is explicitly defined then calling
$declare(<class name> <var name> = new <class name>())
| |
will still succeed and the implicit constructor alone will be called. If arguments are passed
to the constructor then and explicit constructor with matching arguments must be defined
Member variables are retrieved using the dereference operator '.'.
$do( <variable name>.<member variable name> = <value>)
| |
Class member variables are used within method definitions by using the member variable's name.
However, member variables may be shadowed within a method if the method takes an argument or
declares a local variable of the same name. Even this case member variables can still be accessed
by making use of the special variable 'this'. The value of the variable 'this' is always
the instance of the class the method is being called on. Thus within a method definition the expression
'this.<member variable name>
will return the specified member variable. The variable 'this' may not be shadowed or assigned to
or re-declared as a local variable or argument.
Methods are called on Objects by using the dereference operator '.'.
$do( <variable name>.<method name>(<method arguments>))
| |
The $use construct provides access to extension libraries. These libraries, located on disk
under the mx directory of your moto installation, provide definitions for a number of
objects and functions. The extensions included with the moto distribution are
moto | This extension library contains the definition for String objects
as well as object wrappers for the fundamental types (Boolean, Character, Double, Float,
Integer, and Long). This extension is used automatically by moto and does not need to
be explicitly used in a moto page. |
cstdlib | This extension library makes a number of functions from the
C standard libraries available to moto programmers |
codex.util | This extension library provides interfaces to the codex
library of utility objects. These objects include : Enumeration, FileIO, Hashset,
Hashtable, IntEnumeration, Inthashtable, IntSet, IntStack, ItoIHashtable, KMPString,
Stack, StringBuffer, Stringset, SymbolTable, Tokenizer and Vector. |
codex.http | This extension library provides objects for session and
state management in web applications, as well as basic function libraries for
interoperating with the Apache webserver. The interfaces included are: Context, Session
State, and HTTPRequest. |
codex.io | This extension library provides objects for interacting with
a Posix file system. Objects included are: PosixFileSystem.
|
codex.db.mysql | This extension library provides objects for interacting with
a MySQL database. Objects included are: MySQLConnection
and MySQLResultSet. |
codex.db.pgsql | This extension library provides objects for interacting with
a Postgres database . Objects included are: PGConnection and PGResultSet. |
| |
The $use construct can only be placed in the top level scope of a moto page and may not be placed
inside any conditional, iterative, or scoping block.
New extensions can be added by the creation of subdirectories under the mx directory. Objects
may be added to an extension via the creation of an interface (.i) file for that object.
In Moto, arrays are also considered a subclass of Object.
You cannot however instantiate an object of type "Array", rather you must declare
and instantiate an array based upon the sub-type it stores.
$declare(int foo[] = new int[5])
| |
The above example declares an int array 'foo' and then allocates the storage
for five ints with the new keyword
To get or set the value of of one of the indexes in the array you use the subscript operator
'[' ']'
$do(foo[1] = 3)
$(foo[1])
| |
All array indexes start at 0 so in the above example 0 through 4 would be valid indexes
in the array 'foo'. If foo[5] was accessed at runtime an ArrayBoundsException would be thrown.
Only integer
typed expressions can be used for array access, thus in moto only expresions of type int or
type long may be used.
If you know the contents of the array at declaration time you can specify it inline as follows.
$declare(int[] bar = {1,2,3})
| |
Note that the above example is really just shorthand for assigning the array 'bar' new int[3] and
then setting the individual elements of that array to 1,2, and 3 from first to last. The allocation
still occurs dynamically on the heap.
You can use similar syntax to construct instances of pre-defined arrays anywhere in your code.
This is done using the cast operator.
$(new String(<char []>{'h','e','l','l','o',' ','w','o','r','l','d'}))
| |
Moto also allows for the declaration and instantiation of multi-dimensionsal arrays.
$declare(char bar[][] = new char [3][2])
| |
The above example declares the variable bar to be a two dimensional array of characters,
then assigns to it a new two dimensional array for characters size 3 by 2. Multi-dimensionsal
arrays in Moto work the same way they do in Java. Instead of allocating one Object
of size dim 1 * dim 2 * dim 3 etc. multiple Objects are allocated so that the above new statement
actually creates the following 4 Objects:
This allows the flexibility of declaring arrays where the dimensions of nested arrays are
not equal (to represent adjacency lists for example)
$declare(char bar[][] = new char[3][])
$do(bar[0] = new char[3])
$do(bar[1] = new char[1])
$do(bar[2] = new char[2])
| |
Since arrays are objects, sub-arrays of multi-dimensional Array may even correspond to
the same Array
$declare(char bar[][] = new char[3][])
$do(bar[0] = bar[1] = bar[2] = new char[2])
| |
Arrays are passed to Moto functions by reference. Thus moto functions may alter the values
of array subscripts.
$define(void foo(int bar[]))
$do(bar[0] = 27)
$enddef
$declare(int maka[] = new int[3])
$do(foo(maka))
$(maka[0])
| |
Therefore the above example would output '27'.
The length of any array may be retrieved at runtime with the length() function thus
$declare(char bar[][] = new char[3][])
$(length(bar))
| |
would output '3' .
Arrays may be implicitly cast to Objects for the purposes of function calls. Moreover arrays of
subclasses of Objects and multidimensional arrays may be implicitly cast / assigned to
object arrays (Object[]).
Character and byte arrays have special handling in the moto language. Both may be output using the print
$() construct. Both may be appended to Strings with the string addition operators. Both may be converted
to Strings directly using the String constructor or the str() function.
Sometimes things go wrong at runtime. For instance a program might request input from a user that
is used to subscript an array at a certain index, but the index is outside of array bounds. Or
perhaps the array was never initialized and is null. Both of these errors may be checked for ahead
of time in the code, but others are not so easy to check for ahead of time. For instance when a program
performs a long database query and halfway through the connection to the database is severed. Or a
program needs to wrtie a file to disk but the disk is full, or write permissions haven't been
properly granted to the program. In these last two examples the condition that causes the error could either
not have been checked for ahead of time, or there are so many possible reasons the action
might fail that checking for all of them individually just isn't code efficient.
In all of these cases moto will throw an Exception . Throwing and exception is a lot like returning
from a function early. However when an exception is thrown, the active functions or methods on the moto
stack will keep returning until the Exception is caught or until there is no possible code left to catch
them at which point an Uncaught Exception error will be displayed.
What this really boils down to is that if you have a treacherous piece of code that may throw an Exception at
runtime, that you want to be able to deal with, you must wrap that code in a try-catch block. Take
for example the array bounds case from earlier.
$declare(char bar[] = new char[3])
$do(bar[4] = 'd')
| |
The above cade segment will surely fail since we are accessing an array index outside of declared bounds.
However, were we to write the following :
$declare(char bar[] = new char[3])
$try
$do(bar[4] = 'd')
$("I did a bad thing")
$catch(Exception e)
$("I caught an exception!")
$endtry
| |
The above code will not fail but instead will output 'I caught an exception!' . It will not
output 'I did a bad thing' because as soon as the exceptional condition occurred the program started
looking for a catch statement that would handle it.
From the syntax of of the $catch statement you may have already guessed that Exceptions are a type
of Object. The catch statement declares a local variable within the catch block that references the
exception so that important information may be gleaned from it and perhaps presented to the user. In
the previous example we declared the exception e to refer to the Exception that was caught. All exceptions
have at least the following methods :
Method |
Description |
Exception Exception() | Constructs a new Exception object |
Exception Exception(String message) | Constructs a new exception object with the
specified message. |
String toString() | Returns the type of the exception and any message included in the
exception as a String |
String getFile() | Returns the name of the file from which the Exception was thrown |
int getLine() | Returns the line number of the file where the Exception occurred |
String getStackTrace() | Returns a string representation of the call stack as it was when the Exception was thrown |
| |
Re-writing the previous example to use these methods we have:
$declare(char bar[] = new char[3])
$try
$do(bar[4] = 'd')
$("I did a bad thing")
$catch(Exception e)
$("I caught an exception!")
$(e.toString())
$(e.getFile()+":"+e.getLine())
$endtry
| |
This program would output :
ArrayBoundsException : Attempt to subscript array outside of declared bounds
myfile.moto:3
| |
Notice that the type of the Exception is ArrayBoundsException. Yet what we caught was just Exception.
This works because all Exceptions are sub-classes of Exception so simply catching an Exception will
catch all runtime exceptional conditions. If we had wanted too, than in the above catach we could have
caught ArrayBoundsException specifically. Only rarely do you want to catch all possible types of
Exceptions. Rather a program should catch only the Exceptions that the program can handle.
$declare(char bar[] = new char[3])
$declare(String foo = null)
$declare(boolean keepTrying = true)
$declare(int index = 4)
$while(keepTrying)
$try
$do(bar[index] = 'd')
$(foo.length())
$do(keepTrying = false)
$catch(ArrayBoundsException e)
$("I caught an ArrayBoundsException, better set index!")
$do(index=2)
$catch(NullPointerException e)
$("I caught a NullPointerException, better set foo!")
$do(foo="happy")
$endtry
$endwhile
| |
When either an ArrayBoundsException or a NullPointerException is thrown in the above example
the program takes the action neccessary to make sure that they are not thrown again. It keeps looping
until all the statements in the try block execute successfully. This program outputs :
I caught an ArrayBoundsException, better set index!
I caught a NullPointerException, better set foo!
5
| |
What the above program demonstrates is that a program can and should take different actions depending
on the exception that is thrown. It also makes use of the third type of built in moto exception.
While most Exceptions are thrown from extension function or method calls, Moto code may throw the
following types of Exception at runtime :
Exception |
The superclass for all other exceptions, when caught handles all other Exceptions |
NullPointerException |
Thrown whenever an null array is subscripted or a method is called on a null object or a
null object is dereferenced |
ArrayBoundsException |
Thrown whenever an array is subscripted outside its declared bounds. |
MathException |
Thrown whenever a numeric type is divided by zero. |
| |
Perhaps you have written some code that has it's own set of unique exceptional conditions and you
want to throw your own exceptions. This is accomplished by first constructing a new Exception, and
second throwing it with the $throw construct.
$throw(new Exception("You shouldn't have done that"))
| |
You may also throw any of the Exception subtypes loaded from different extensions. A common
exception you might throw is an IllegalArgumentException. It should be thrown when a function or
method gets passed an argument that it is not designed to work with.
$define(String getAreaCode(String phoneNumber))
$declare(Regex phoneRx = /[(]([0-9][0-9][0-9])[)][0-9][0-9][0-9][\-][0-9][0-9][0-9][0-9]/)
$if(phoneNumber == null)
$throw(new IllegalArgumentException("The phone number string provided may not be null"))
$endif
$declare(Match match = phoneRx.match(phoneNumber))
$if(match.subMatch(1) == null)
$throw(new IllegalArgumentException("The phone number provided is in an incorrect format"))
$endif
$(match.subMatch(1))
$enddef
| |
Often times it is important to have operations that are guaranteed to take place regardless of the success
or failure of the preceeding operations. A common example of this occurs when dealing with pools of
objects. The methodolgy for using an object pool is to grab an object out of it, call some methods on
that object, and when you're done, return the object to the pool. During the course of calling methods
on the object an exception may be thrown. Some types of exceptions you might want to catch, others you
might not. In all cases you want to make sure that the object gets returned to the pool. If it is not
your program will have a resource leak.
To specify code to execute regardless of the success or failure of the code you're trying to execute
you must use the $finally keyword.
$try
code we want to try to execute
$catch(FooException f)
code to be executed if a FooException got thrown
$catch(BarException b)
code to be executed if a BarException got thrown
$finally
code we want executed regardless of whether an exception occurred even if we caught it
$endtry
| |
A finally block gets executed no matter what. If the try block is inside of a function call
and the function returns from inside the try block or a catch block, the finally block will still get
executed. If the try block is inside a loop and the break or continue statement is used, the finally block
code will still get executed. If an exception gets thrown but not caught and passes through three different
try-catch-finally blocks, the finally blocks of all three will be executed.
The semantics of Moto Exception handling are the same as Exception handling in Java. An exception,
once thrown will jump to the end of the most recently opened try block. Its type will be compared
with the types of exceptions caught in any catch block opened after that try block. If its type
matches than that exception is said to have been handled and the code for that catch block is executed.
Regardless of whether the exception was handled or not, or whether or not there was an exception, the
finally block code, if present, is then executed. If there was an exception thrown and it was not handled
then once any finally block code has been executed the exception is re-thrown.
If an exception is thrown from within a catch block, and it is not handled (by way of a nested try-catch block)
than that exception overrides the current exception. However exceptions thrown from within catch blocks
are not compared with the types of Exceptions handleable by the current try-catch block. Rather the finally
code is executed and the new exception is re-thrown.
If an exception is thrown from within a finally block, and it is not handled by way of a nested try-catch block,
than that exception overrides the current exception if there is one and it is immediately re-thrown. If an exception
is generated from within a finally block but is handled by that same finally block (by way of a nested try catch) but
another exception had already been generated and not handled before entering the finally block, than after finally
block execution the original exception is re-thrown.
If a break, return, or continue is generated from within a try block than the finally block is executed and
the break, return, or continue action takes place. If however within the finally block there is a different sort
of jump statement such as another break, return, or continue, or even an exception getting thrown, than that action overrides
the action specified within the try block. If a break, return, or continue action is generated from within a catch
block than it can still be overridden by an action in the finally block.
Moto supports an additional, more compact syntax for embedding large
'blocks' of code into a page without worrying about whitespace treatment. This
syntax may be used in addition to the single construct syntax described in the
previous section.
Embedded blocks begin with the escape sequence ${ and ends with }$. Inside and
embedded block you can have multiple moto statements. Each statement must be terminated
by a semicolon ';' .
${
statement 1;
statement 2;
statement 3;
}$
| |
To comment out a block of text you want moto to ignore from within an
ebmedded block surround that text with /* and */ .
You can also comment out all text until the next end of line by preceeding the
comment with //
print "hello"; // this statement prints "hello" | |
Inside of an embedded block you can declare variables just as you can in C or java
variable type variable name ( = optional variable value )
| |
The embedded block syntax for declarations is much more compact than with $declare()
${
Date d = new Date();
String month = getValue("_month",str(d.getMonth()+1));
String day = getValue("_day",str(d.getDayOfMonth()));
String year = getValue("_year","2002");
int i;
String curtok;
Tokenizer stok = new Tokenizer("Jan|Feb|Mar|Apr|Mar|Jun|Jul|Aug|Sep|Nov|Dec",'|');
}$
| |
Every statement inside an embedded block is essentially a $do() statement. Anything
you would use a $do() construct for you can write as a statement inside an
embedded block.
${String foo = "bar"; int l; l=foo.length() ; l++; foo=foo+foo; }$
| |
You can print something out from an embedded block statement by preceding your expression
with the print keyword.
${print 7.00e+10 ;}$
${print 7.00E+10 ;}$
${print 7.00e-10 ;}$
${print 7.00E-10 ;}$
${print false; }$
${print 7.0F; }$
${print 7.0f; }$
${ print 7;}$
${
print 7L;
print "\n";
print 7l;
}$
${print null;}$
${print "string";}$
| |
If statements in embedded blocks work just like they do in C or Java
${
String qryQuestions;
if (idTopic eq "0")
qryQuestions =
"SELECT q.*,iu.imageURL,p.uname FROM "+
"tblQuestion q LEFT OUTER JOIN tblImageURL iu ON q.idImageURL=iu.idImageURL "+
", tblPrincipal p WHERE p.idPrincipal=q.idOwner";
else if (idTopic eq "-1")
qryQuestions =
"SELECT q.*,iu.imageURL,p.uname FROM "+
"tblQuestion q LEFT OUTER JOIN tblImageURL iu ON q.idImageURL=iu.idImageURL " +
"LEFT OUTER JOIN tblTopicToQuestion ttq ON ttq.idQuestion = q.idQuestion "+
", tblPrincipal p WHERE p.idPrincipal=q.idOwner AND ttq.idTopic is null ";
else
qryQuestions =
"SELECT q.*,iu.imageURL,p.uname FROM "+
"tblQuestion q LEFT OUTER JOIN tblImageURL iu ON q.idImageURL=iu.idImageURL " +
", tblTopicToQuestion ttq, tblPrincipal p WHERE ttq.idQuestion = q.idQuestion "+
"AND ttq.idTopic = "+idTopic+" AND p.idPrincipal=q.idOwner";
}$
| |
There is no elseif keyword in embedded blocks, rather the else matches up with
the most recent if
The switch statement works similar to switch in C or Java but like in the
$switch() construct, cases do not fall through. Unlike the $switch()
construct however, the embedded switch statement does make use of a
default keyword. It must however follow all cases in the switch block.
${foo="bang";
switch(foo){
case "bar": print "baz";
case "maka": print "ok";
case "bling": print "bing";
default: print "zip";
}
}$
| |
For loops in embedded blocks have the same syntax as they do in C
${for(i=0;i<10;i++) print "hello "+str(i)+"\n"; }$
| |
So do While loops
${i=0; while(i<10) print "hello "+str(i++)+"\n"; }$
| |
Break and continue are used just like any other statement
Embedded while
${i=0;
while(true)
if (i<10)
print "hello "+str(i++)+"\n";
else
break;
print "goodbye\n";
}$
Embedded continue
${i=0;
while(true) {
if (i<10) {
print "hello "+str(i++)+"\n";
continue;
}
break;
}
print "goodbye\n";
}$
| |
Within embedded blocks you can create explicit variable scopes by using the '{' and '}'
operators. Again this works just like it does in C or Java. Declarations inside an embedded scope
only exist within that scope. For example the following piece of moto code
${
int j = 1;
{
int j = 1;
print ++j; print "\n";
}
print j;print "\n";
}$
| |
Would output
From the above example you can see also that redeclaration of an existing variable
inside an embedded scope shadows the previous declaration.
You can also use moto scopes to escape from embedded blocks allowing you to intersperse
static text or classic moto constructs with your embedded block structured code
${ i=0;
while(true)
if (i<10) {
}$
hello $(i++)
${
} else {
}$
$break
${
}
print "goodbye\n";
}$
| |
They syntax for defining a function in an embedded block is the same as it is in C or Java
type name(arguments){
...
return expression;
}
| |
The to use the global type qualifier, just preceed the variable declaration with the keyword
global
The following example defines two functions and declares three global variables
${
global Vector debugQueries = new Vector();
global Vector debugQueryTimes = new Vector();
global Vector debugQueryResults = new Vector();
MySQLResultSet doQuery(MySQLConnection conn, String qry){
MySQLResultSet r;
long t;
t=time();
r = conn.query(qry);
t=t-time();
debugQueries.add(qry);
debugQueryTimes.add(new Long(t));
debugQueryResults.add(new Integer(r.size()));
return r;
}
void doDebugQueries(){
int i;
for(i=0;i<debugQueries.size();i++){
print "<p><pre>";
print <String>debugQueries.get(i);
print "</pre>\n";
print "Results Returned: "+str((<Integer>debugQueryResults.get(i)).getValue()) + "<br>\n";
print "Execution Time: "+str((<Long>debugQueryTimes.get(i)).getValue()) + "<br>\n";
}
}
}$
| |
The syntax for defining a Class in an embedded block is the same as it is in Java
class class name {
member variable declarations
method definitions
}
| |
The following example defines two classes
${
class Question {
int idQuestion;
String qText;
int idImageURL;
int level;
String explanation;
int numanswers;
int answers[];
int canswer;
int numtopics;
int topics[];
}
class QuestionManager {
Question get(int idQuestion){
int i;
MySQLResultSet rset;
Question q = new Question();
rset = doQuery(conn,
"SELECT q.* FROM tblQuestion q WHERE q.idQuestion = "+str(idQuestion));
if(!rset.next())
return null;
q.idQuestion = idQuestion;
q.qText = rset.getString("qText");
q.idImageURL = rset.getInt("idImageURL");
q.level = rset.getInt("level");
q.explanation = rset.getString("explanation");
rset=doQuery(conn,
"SELECT a.idAnswer, qta.isCorrect " +
"FROM tblQuestionToAnswer qta, tblAnswer a WHERE " +
" qta.idQuestion = "+str(idQuestion)+ " AND " +
" a.idAnswer = qta.idAnswer " +
"ORDER BY a.aText"
);
q.numanswers = rset.size();
q.answers = new int[rset.size()];
for(i=0;i<rset.size();i++){
rset.next();
q.answers[i]=rset.getInt("idAnswer");
if(rset.getInt("isCorrect")==1)
q.canswer=rset.getInt("idAnswer");
}
rset=doQuery(conn,
"SELECT ttq.idTopic " +
"FROM tblTopicToQuestion ttq WHERE " +
" ttq.idQuestion = "+str(idQuestion)
);
q.numtopics = rset.size();
q.topics = new int[rset.size()];
for(i=0;i<rset.size();i++){
rset.next();
q.topics[i]=rset.getInt("idTopic");
}
return q;
}
}
}$
| |
The syntax for try catch and finally is the same as it is in Java
try { ... }
catch (NullPointerException npe) { ... }
catch (Exception e) { ... }
finally { ... }
| |
The syntax for throw is the same as well
Regular expression support is built into the moto language. Regular expressions in moto are specified by
a forward slash '/', followed by the regular expression definition, followed by a forward slash. An example
regular expression would be /([a-zA-Z]+)@(([a-zA-Z]+[.])*[a-zA-Z]+)/ which would match for simple email
addresses. Regular expressions in moto have the type Regex. New regular expressions may be constructed dynamically
from Strings by calling a constructor.
$declare(Regex emailrx = new Regex("([a-zA-Z]+)@(([a-zA-Z]+[.])*[a-zA-Z]+)"))
| |
The String used for creating a regular expression must not contain opening and closing slashes.
As of moto version 0.12.0 regular expression functionality is now implemented by a variant of Ville Laurikari's
excellent TNFA matching algorithms.
Regular expressions are specified with their own min-language within moto. Standard moto operators do not
apply or work differently when constructing regular epxressions.
The operators currently available for use in specifying a regular expressions are :
+ | one or more of the immediately preceding character or subexpression |
* | zero or more of the immediately preceding character or subexpression |
? | zero or one of the immediately preceding character or subexpression |
. | matches any single character |
| | matches the expression that comes before the | or the expression that comes after it |
\ | treats the following character as literal except for \n \r and \t which match
newline, carraige return, and tab respectively |
(...) | designates a subexpression. The values of matched subexpressions may
be retrieved after a match is made by calling methods on the Match object |
[...] |
designates a set of acceptable characters (character class) e.g. [1234567890] would specify that any
numeric character was accepted.
The - operator may be used inside a character class to specify a range of accepted characters
e.g. [1-9A-Za-z] would specify all numbers and letters as acceptable.
Starting a character class with a ^ negates the class, matching all characters or ranges not
specified within it.
The \ operation also works within character classes the same as it does outside of them.
|
| |
As was noted earlier there are operators built into that can test whether a regular expression is found within a
String.
A =~ B |
the regular expression B matches a substring of the String A |
A !~ B |
the regular expression B does not match any substring of the String A |
| |
These operators are used for searching and only return true or flase as to whether some substring of A matched B.
To find out whether a regular expression matches a String or whether it mathes a prefix or suffix of a String
one must use methods of the Regex object.
Method |
Description |
boolean matches(String s) |
Returns true if the specified string is matched by this regex |
boolean matchesPrefix(String s) |
Returns true if the specified string starts with a prefix that
is matched by this regex |
boolean matchesSuffix(String s) |
Returns true if the specified string ends with a suffix that
is matched by this regex |
boolean matchesSubstring(String s) |
Returns true if the specified string contains any substring that
is matched by this regex. This method returns the same results as using the =~ operator. |
| |
These methods are valuable for input validation e.g. you request that a user input an email address and you
want to verify that what the user entered is at least of the form of a valid email address.
Often times however it is not enough to know that an input String matched or contained a regular
expression. Often one is interested in the details of how the match occurred. This information includes
where in the
input String the match was found, how long the match was, and what the contents of the matched
subexpressions were.
This information is retrieved via Regex methods which return Match objects.
Method |
Description |
Match match(String s) |
Returns a Match object for the specified string matched by this regex |
Match matchPrefix(String s) |
Returns a Match object for the specified string is matched by this regex requiring
only the a prefix of s matches |
Match matchSuffix(String s) |
Returns a Match object for the longest suffix of s matched by this regex |
Match search(String s) |
Returns a Match object for the leftmost longest match of this regex in s |
| |
With a Match object in hand one can call methods on that object to return the relevant details of
the match.
Method |
Description |
String Match::subMatch(int i) |
Returns the substring of the matched string corresponding to the i'th subexpression of
the regular expression used. The i'th subexpression is the subexpression started with the i'th
opening parenthesis in the regular expression. Passing 0 to this method will return the entire
string matched |
String preMatch() |
Returns the substring of the String matched against occurring prior to the start of the match |
|
String postMatch() |
Returns the substring of the String matched against occurring after the end of the match |
int startIndex(int i) |
Returns the index in the String matched against where the i'th subexpression began. If
i is set to 0 this method returns the index in the original String where the match begins |
int endIndex(int i) |
Returns the index in the String matched against where the i'th subexpression ends. If
i is set to 0 this method returns the index in the original String where the match ends |
| |
In the following example a Match object is used to pick out the pieces of an email address found in a string of
text. The subexpressions of interest in this case are the name and the host in the email address.
${
Regex rx = /([a-zA-Z]+)@(([a-zA-Z]+[.])*[a-zA-Z]+)/;
String i2 = "email me at dhakim@webcodex.com!";
Match m = rx.search(i2);
print "Match was '"+m.subMatch(0)+"'\n";
print "Name was '"+m.subMatch(1)+"'\n";
print "Host was '"+m.subMatch(2)+"'\n";
print "Pre-match was '"+m.preMatch()+"'\n";
print "Post-match was '"+m.postMatch()+"'\n";
print "Match was found at <"+m.startIndex(0)+","+m.endIndex(0)+"> in the input string\n";
}$
| |
The output of this example is
Match was 'dhakim@webcodex.com'
Name was 'dhakim'
Host was 'webcodex.com'
Pre-match was 'email me at '
Post-match was '!'
Match was found at <12,31> in the input string
| |
The most powerful uses of regular expressions are for String manipulation. String manipulation
with regard to regular expressions usually refers to two main activities :
- Using regular expressions to split a string wherever a matching pattern is found. This is known
as tokenizing or splitting a string.
- Replacing all instances of a pattern in a String with something else. This is known generally as
substitution.
Suppose you have an input string that you want split by some pattern. Lets take as an example
a string containing a numbered list of elements. The way you would do this in moto is by constructing a
Tokenizer object from the input string
and pattern. The pattern for a number followed by a period would be /[0-9]+\./ . Calling the next() method
on the Tokenizer will return the next substring of the input string that does not contain the specified
pattern. Calling next() repeatedly will continue doing this up until there are no further such substrings
at which point next() will return null.
${
use "codex.util";
Regex rx=/[0-9]+\./;
String s="1. foo 2. bar 3. maka";
Tokenizer t= new Tokenizer(s,rx);
String c;
for(c=t.next();c != null;c=t.next())
print "got '"+c+"'\n";
}$
| |
The output of this program would be
got ' foo '
got ' bar '
got ' maka'
| |
Regular expression substitution is much more powerfull than the 'find and replace' functionality found in
most word processors for two reasons:
- Patterns are matched as opposed to static strings.
- The substitution can make use of submatch information.
The following are some common examples of how substitution by regular expressions are used
- Escaping HTML
- Auto-markup of URLs
- Emoticonizing
- Colorizing source code
- Implementing find and replace
- Starring out dirty words
Most but not all of these examples can be accomplised by using the built in function sub(). The sub()
function takes three arguments. The first is the input string. the second is pattern in the string we want
to replace. The final argument is a substitution string. Thus if I wanted to replace all instances of the
word "foo" with "bar" in my input string I would do that as follows :
$do(sub(input,/foo/,"bar"));
| |
A more usefull example would be using sub() in a function to escape reserved HTML characters.
String st_escapeHTML(String input){
return sub(sub(sub(input,/&/,"&"),/>/,">"),/</,"<");
}
| |
The sub function is capable of far more complex substitutions however because the substitution string
can itself refer back to last match made. This is done by including variables in the substituion string
of the form $# where # is the number of the subexpression in the regular expression you wish to substitute.
$0 will substitute the entire last match. A concrete example of when to use this functionality is when
doing auto-markup. Auto is the process of finding hyperlinkable strings in text and generating the HTML
code around those strings to link them.
${ {
Regex rx = /([a-zA-Z]+)@(([a-zA-Z]+[.])*[a-zA-Z]+)/;
String substitution = "<a href=\"mailto:$0\">$1 at $2</a>";
String input = "dhakim@webcodex.com is my email, my email is dhakim@webcodex.com";
print "'"+input+"'"; print " =>\n\t"; print sub(input,rx,substitution); print "\n";
} }$
| |
This script outputs :
'dhakim@webcodex.com is my email, my email is dhakim@webcodex.com' =>
<a href="mailto:dhakim@webcodex.com">dhakim at webcodex.com</a> is my email, my email is <a href="mailto:dhakim@webcodex.com">dhakim at webcodex.com</a>
| |
In Moto, functions and methods have types and may be assigned to variables of the same types. These 'functional' variables may themselves be treated as functions. Thus functions and methods may be passed as arguments to other functions or methods. Functions may even be returned from other functions or methods. New functions can even be constructed at runtime by 'applying' some or all of the arguments of existing functions.
Arrays are the simplest example of implicitly valid types in Moto. Given a valid type X, X[] is a valid type in Moto. Specifically X[] represents an array of X's . And X[][] is a type representing a two dimensional array of X's. This sort of dynamic type validation is common to many languages including Java.
There is another class of types in Moto that are 'implicitly valid' as long as their base types are valid. These are called functional types which can be described as follows:
- If X is a valid type X() is a valid type representing a function which takes no arguments and returns a value of type X
- If X and Y are valid types X(Y) is a valid type representing a function which takes an argument of type Y and returns a value of type X
- If X is a valid type and Z is comma delimited list of valid types than X(Z) is a valid type representing a function which takes arguments of the types listed in Z and returns a value of type X
Here are some concrete examples of declarations using functional types:
int() f; // f can be assigned a function which takes no arguments and returns an int
int(int) g; // g can be assigned a function which takes an int and returns an int
int(int,String) h; // h can be assigned a function which takes an int and a String and returns an int
int(int()) i; // i can be assigned a function which takes a function that takes no arguments but returns an int. i must also return an int.
int(Vector(int),String()) j;
Enumeration(StringBuffer(int),String())(String(),float) k;
Object(int[](StringBuffer[][]),String())(String[](),float[]) l;
| |
As you might have guessed by the name (and the above examples) functionally typed variables can be assigned real functions or methods who's arguments and return types match. Consider to the function atoi in cstdlib which takes a String and returns an int. We could define a functional variable 'myatoi' and assign it atoi:
int(String) myatoi = atoi;
| |
Functional variables can be called just like regular functions and methods.
The type of a function or method in Moto is
- The function or method's return type
- followed by '('
- followed by the types of each of the arguments it takes separated by commas
- followed by ')'
Simple ... no ?
The atoi example is simple in that there is only one function in the system named atoi. Moto however, like C++ and Java, allows for multiple functions to have the same name but take different arguments. A good example of this is the getValue function from the codex.http library. There are two variants of getValue, one that takes one argument, and one that takes two. We can differentiate between the two by using the '?' or 'unknown argument' token.
String(String) myGetValue = getValue(?);
String(String,String) myGetValueWTwoArgs = getValue(?,?);
| |
We can be even more specific by using casts in addition to the '?' token. Suppose we have two functions with the same name that take a single argument (but of two different types) :
void println(int i){ print i+"\n"; }
void println(String s) { print s+"\n"; }
| |
We can specify which one we want to identify by casting the unknown:
void(int) myIntPrintln = println(<int>?);
void(String) myStringPrintln = println(<String>?);
| |
The last thing worth noting here is that you can optionally use the '&' operator to make clear that you want to identify a function. The following are examples of fully qualified function identification:
void(int) myIntPrintln = &println(<int>?);
void(String) myStringPrintln = &println(<String>?);
| |
The real reason for the '&' becomes clear in relation to partial application discussed later. For the sort of regular function identification that has been described using the '&' or 'function identification' operator can improve code readability but is generally optional. The only case where it is necessary is when we have multiple variants of a function with the same name and we need to identify the no-arg variant:
void println(int i){ print i+"\n"; }
void println(String s) { print s+"\n"; }
void println(){ print "\n"; }
void() myPrintln = println; // No good ... this is ambiguous
void() myPrintln = &println(); // Perfect ... this is fully qualified identification
| |
Since functional types are no different from other types, creating functions that take functions as arguments is done just like you might suspect. The following function takes an array of Strings and a function to perform on each element of the array:
void each(String[] a,void(String) f){ for (int i=0;i<length(a);i++) f(a[i]); }
| |
We could pass this function a function println
void println(String s) { print s+"\n"; }
| |
To print out all the elements in an array of Strings :
each({"one","two","three"}, println);
| |
The Enumeration class has an each() method built in which calls the function passed to it on each element in the enumeration. Thus to print out all the keys in a hash you could now just write
stab.keys().each(<void(Object)>println);
| |
This is as opposed to
Enumeration e = stab.keys();
while(e.hasNext())
println(<String>e.next());
| |
Which is the 'non-functional' (Java) way of doing things.
Notice the explicit cast to type 'void(Object)' in the functional example . That is needed because that is the type of the function the each method expects to be passed while println has type 'void(String)'. Sometimes functions can be cast 'implicitly' but this is not one of those times.
No example of passing functions to functions would be complete without the classic example of sorting. Vectors have a sort() method. This method takes a function which can be used to compare two Objects in the Vector. Why does it need to take a function you ask ? Because there is no way to compare two arbitrary objects.
The function must take two Objects and return 0 if they are equal, 1 if the first one is 'greater' than the second, -1 if the first one is 'less' than the second. What 'less','greater', and 'equal' really mean in this context is that you want the the first element to appear before, after, or next to the second element in the final sorted list.
Luckily the is a function that does just that for Strings in the cstdlib extension called strcmp.
Thus to sort a Vector of Strings in Moto all you need to do is say
v.sort(<int(Object,Object)>strcmp)
| |
Again we need to explicitly cast strcmp to 'int(Object,Object)' from 'int(String,String)' . The reason is that the Vector doesn't know it's holding Strings. For all it knows it's holding Dates or any other kind of Object and so it expects functions that work on 'Object's and not sub-classes of Object. If it were holding Dates however you could sort them as follows:
int dcompare (Object a,Object b){
Date l = <Date>a, r=<Date>b;
return atoi(l.format("%Y%m%d")) - atoi(r.format("%Y%m%d")) ;
}
v.sort(dcompare);
| |
You don't need to explicitly cast functions (or functionally typed expressions) when assigning them or passing them to functions when they can be implicitly cast. The implicit casting rules for functional types are as follows:
- Like all reference types, (yes, functional types are reference types) values that have functional types can be implicitly cast to 'Object'.
- Any type X(...) can be implicitly cast to void(...) since 'void' really means we don't care what if anything the function returned.
- Any type X(...) can be implicitly cast to Y(...) as long as X is a descendent of Y. The reason is if we have a function foo, which for the sake of argument takes no arguments and returns a String, and we assign it to a variable of type 'Object()' that assignment should be valid since everything foo could return would also be an Object.
- Any type X(... Y ...) can be implicitly cast to X(... Z ...) as long as Y is an ancestor of Z. Suppose we have a variable of type 'void(String)'. This functional variable must be callable with any String. If we assigned to it a function of type 'void(Object)' than the requirement that 'any function assigned to it must handle all values of type String' is not broken. The function we assigned to it can handle all Strings and then some.
The explicit casting rules for functionally typed expressions are like the implicit casting rules and their mirror image.
- If X can be implicitly cast to Y then X can be explicitly cast to Y
- If X can be implicitly cast to Y then Y can be explicitly cast to X
In cstdlib there is a function pow
double pow(double x, double y)
| |
which returns x to the power y (xy). Suppose we wanted to define a function square() which squares (x2) its input. We could say:
double square(double x) { return pow(x,2); }
| |
Or we could do this:
double(double) square = pow(?,2);
| |
What we've done is created a new function by partially applying the arguments to a function. We could partially apply the other argument to create a function twoToThePow (2x)
double(double) twoToThePow = pow(2,?);
| |
We could even apply both arguments to give us a function that returns the same thing all the time:
double() nine = pow(3,2) // No no no ... this won't work
| |
The above actually generates an error saying 'Expression of type double cannot be assigned to variable of type double()' . That's because moto thinks the expression 'pow(3,2)' is a function call, not a function identifier. To clarify the situation you need to use the '&' operator.
double() nine = &pow(3,2) // Bingo!
| |
Suppose we have a Vector v. We can say
void(Object) add = v.add;
| |
The above defines a new function add() which, every time it is called, adds an Object to v. This is actually a powerful example of partial application. Behind the scenes all methods secretly take the instance of the object they are called on as an argument. By identifying 'v.add' we are saying we want the 'add method on the Vector currently assigned to v'. If v were to be assigned a different vector later on the add function we just defined would still add objects to the original Vector.
So what makes this so powerful ? Lets look at some examples
// Cloning a Vector
v1.elements().each(v2.add);
// Pruning a Vector of duplicates
Stringset s1 = new Stringset()
v1.elements().each(<void(Object)>s1.add);
s1.elements().each(v2.add);
// Printing out the elements in a hash (SymbolTable)
void outputNV(Object n,SymbolTable s) { print <String>n +" => "+<String>s.get(<String>n)+"\n"; }
stab.keys().each(outputNV(?,stab));
// Inverting a hash
void valueToKey(Object oldkey, SymbolTable src, SymbolTable dest) {
dest.put(<String>src.get(<String>oldkey),oldkey); }
SymbolTable namesur = new SymbolTable();
surname.keys().each(valueToKey(?,surname,namesur));
| |
As in C++, operators in moto can be overloaded giving moto programmers concise semantics for expressing common operations on objects. When an operator for a class is overloaded moto programmers can write code using operators that expands behind the scenes to function and method calls. For example the moto code A[0]=B+C may be used as short hand for A.setAt(0,union(B,C)) depending on the types of the variables A,B, and C.
The String class is an example of a moto class that makes use of operator overloading. String addition with the + and += operators, or String comparison with the eq, ne, lt, gt, lte, and gte operators is not built in to the moto language. Rather it is implemented through the use of overloaded operators.
The moto utility classes (from the moto.util) extension make the widest use of this facility. You can set and retrieve objects from vectors and hashes using array notation. You can union or intersect sets using the + and * operators. You can even compare the contents of most any two objects of the same type with the 'eq' and 'ne' operators.
A primary use of overloaded operators is to make commonly performed operations on commonly used objects appear 'first class' in the language.
This is the case with Vectors and Symboltables (hashes). This is due to their relation to Arrays.
Arrays are a first class feature of most all programming languages. Thus they are operated on by operators. Specifically, the [] and []= operators are used to set and get elements in an array.
$do(my_array[13] = "foo")
$return(my_array[27])
| |
Vectors (from the codex.util extension) are just like arrays except they are not bounded and can grow as new objects are added to them. In both Java and Moto a Vector's contents can be set and retrieved through methods:
$do(my_vector.set(13,"foo"))
$return(my_vector.get(27))
| |
However since a Vector is so much like an Array it makes sense that it should be accessible with the same concise operator syntax. Because of operator overloading, in Moto you can treat a Vector like an array using the very same syntax:
$do(my_vector[13] = "foo")
$return(my_vector[27])
| |
SymbolTables are also a lot like arrays except they are unbounded and have keys that are Strings. But since they are so much like arrays it once again makes sense that programmers should be able to access them with the same semantics:
$do(my_symboltable["foo"] = "bar")
$return(my_symboltable["maka"])
| |
You can find out what operations are overloaded for a particular class by looking at the class's interface. In the 'Method operators' and 'Related Functions operators' sections you will find all the overloaded operators for class:
StringSet
|
Method operators |
StringSet StringSet::+=(StringSet i)
Returns the union of two StringSets |
StringSet StringSet::*=(StringSet i)
Returns the intersection of StringSet i1 and StringSet i2 |
StringSet StringSet::-=(StringSet i)
Returns the difference StringSet i1 and StringSet i2 |
Related Functions operators |
StringSet +(StringSet s1,StringSet s2)
Returns the union of two StringSets - {x:x is in s1 or x is in s2 } |
StringSet *(StringSet s1,StringSet s2)
Returns the intersection of two StringSets - {x:x is in s1 and x is in s2 } |
StringSet -(StringSet s1,StringSet s2)
Returns the difference StringSet i1 and StringSet i2 - {x:x is in s1, x is not in s2} |
|
What the above interface is saying is that there are six overloaded operations having to do with StringSets. The + and += operations have been overloaded to mean set union when applied to StringSets. The * and *= operations have been overloaded to mean set intersection . The - and -= operations have been overloaded to mean set difference .
There is a difference between an between an Object having the same content (or contents) as another object, and two variables referring to the same object.
If one were to create two vectors and add the String "foo" to both of them they would be equal in the sense that they are both vectors of size 1 with the String "foo" inside of them.
Vector v1 = new Vector();
Vector v2 = new Vector();
v1.add("foo");
v2.add("foo");
print v1 eq v2; // outputs true
print v1 == v2; // outputs false
| |
However vectors v1 and v2 in the above example are not the same object. They have seperate storage in memory and can be altered independantly of one another. If however we re-wrote the above example as
Vector v1 = new Vector();
Vector v2 = v1;
v1.add("foo");
v2.add("foo");
print v1 eq v2; // outputs true
print v1 == v2; // outputs true
print v1.toString() // outputs {"foo","foo"}
| |
Then v1 and v2 would be reffering to the same object. In this example only one object was ever created. Adding "foo" to v1 and then adding "foo" to v2 added two "foo"s to the same Vector!
This is the difference exposed by the == and eq operators. The eq operator will output true if two objects are equal, the == operator will output true if its operands are two references to the same object.
For this reason the == and != operators are never overloaded since there meaning is already well defined for all classes of objects. However the eq operator is overloaded for most every type of object and can be used to tell if the contents of two objects are equal.
Both the moto module, and any of your own moto applications that you compile with mmc,
run as dynamically loaded modules within the apache webserver. Thus they may
be configured by modifying the httpd.conf file, they access HTTP Requests and Responses
, they can parse HTTP headers and form submissions for cookies and
file upload, via included APIs. To make use of these APIs from within your moto pages
you must
Both the moto module, and any moto application compiled with mmc, may be configured in
your httpd.conf . Basic configuration of your httpd.conf is necessary just to load the
moto module:
LoadModule moto_module libexec/mod_moto.so
AddHandler moto .moto
| |
Or to load a compiled moto module
LoadModule histoexam_module libexec/mod_histoexam.so
<IfModule mod_histoexam.c>
<Location /histoexam1>
SetHandler histoexam
HistoexamOption Location /histoexam1
</Location>
</IfModule>
| |
In the above example there is an Apache directive you probably haven't seen before
HistoexamOption Location /histoexam1
| |
All compiled modules get a new directive that can be used to pass configuration information
into the module. The directive is {Module Name (with the first letter capitalized)} + 'Option'
(hereafter reffered to as ModOption) .
Using a ModOption directive you can pass arbitrary configuration parameters into your module. In the
above example the parameter 'Location' is being set. This parameter is present in all compiled
moto modules and allows for them to be relocated elsewhere in your website. You can use ModOptions
to set an arbitrary number of different parameters and just like other Apache directives, parameters
nested within Location or Directory blocks take precendence over those outside and apply only
inside those Directory or Location blocks.
<IfModule mod_iolio.c>
IolioOption SharedHeapSize 128M
<Location /iolio2 >
SetHandler iolio
IolioOption Location /iolio2
IolioOption DBName iolio1
IolioOption DBUser maka
IolioOption DBPassword shlaka
</Location>
<Location /iolio3 >
SetHandler iolio
IolioOption Location /iolio3
IolioOption DBName iolio2
IolioOption DBUser foo
IolioOption DBPassword bar
</Location>
</IfModule>
| |
To set parameters for the moto module (or for your application while it's still in
development) use the directive 'MotoOption'.
<IfModule mod_moto.c>
MotoOption SharedHeapSize 128M
MotoOption SignalsThrowExceptions true
AddHandler moto .moto
<Location /iolio >
MotoOption DBName iolio
MotoOption DBUser root
MotoOption DBPassword macamaca
</Location>
</IfModule>
| |
To access options set through this mechanism in your moto application use the function
getConfigOption().
$* Set up the database connection *$
$declare(MySQLConnection conn = new MySQLConnection(
"",
getConfigOption("DBName"),
getConfigOption("DBUser"),
getConfigOption("DBPassword")
))
| |
What follows is a list of configuration options that are built in to either the moto
module or compiled modules
Directive | Valid For |
Scope | Description |
Location | mmc compiled modules |
location or directory block |
Sets the path prefix that pages within the compiled moto directory expect
prior to the page name in the URL |
SharedHeapSize | mod_moto and mmc compiled modules |
global | Sets the size of the shared heap. |
SignalsThrowExceptions | mod_moto and mmc compiled modules |
global | Catches SIGSEGV and SIGBUS if raised during page interpretation and
re-throws them as exceptions. |
MXPath | mod_moto |
global | Lets you specify the paths that the moto interpreter will search for moto
extensions. The value should be a ':' delimited list of paths. |
LockDir | mod_moto and mmc compiled modules |
global | Lets you specify the directory that the .lock files used for concurrency control
by the memory manager and context will be dropped off (the default is /tmp) |
Session.Disable | mod_moto and mmc compiled modules |
location or directory block | Lets you turn off session tracking for location in which the directive applies. |
Session.SetSSIDCookie | mod_moto and mmc compiled modules |
location or directory block | Lets you identify the current session via a cookie stored on the client browser.
If the sid state variable is also present than the session identified by the sid state variable takes precedence.
|
Session.MatchSSIDCookie | mod_moto and mmc compiled modules |
location or directory block | If the sid state variable is
also present in the URL then both sid and the ssid must agree thus providing a limited defense against
'session hijacking'. This option is only usefull in combination with the Session.SetSSIDCookie option. |
Session.MatchClientIP | mod_moto and mmc compiled modules |
location or directory block | Including this option means that all requests for a given session must come from the
same IP address that started the session thus providing a limited defense against
'session hijacking'. |
Session.Timeout | mod_moto and mmc compiled modules |
global | Specifies the number of seconds of inactivity before a session will be 'timed-out'. |
| |
Moto currently has basic support for letting you inspect the HTTPRequest. You get the request by calling the
getRequest() function defined in codex.http. Once you have it you can call a variety of methods on it.
Method |
Description |
String getHostname() | Returns the hostname of the requested URL |
String getURI() | Returns the path part of the URL (after the host name but before the ?) |
String getFileName() | Returns the full path on disk of the reuested page |
String getMethod() | Returns the method by which the page was requested
e.g. "POST" or "GET" |
String getProtocol() | Returns the protocol by which the page was requested e.g. "HTTP/1.1" |
String getHeader(String header) | Returns the value of the specified header e.g. getRequest().getHeader("User-agent") might return "Mozilla/4.5 (compatible; OmniWeb/4.1-beta-1; Mac_PowerPC)" |
Cookie getCookie(String name) | Returns the Client Side cookie with the specified name |
Enumeration getCookies() | Returns an enumeration of all the cookies that were parsed from the HTTPRequest |
Enumeration getMIMEParts() | Returns an enumeration of all the 'MIMEEntity's submitted to this page.
Mime parts get submitted on file upload forms (or any form with enctype="multipart/form-data") |
MIMEEntity getMIMEPart(String name) | Returns the MIME part with the specified name. Mime parts get submitted on file upload forms. This function can be dangerous if you've specified a form with two fields named the same. This function will get you the first one only. |
| |
Moto supports the setting and retrieval of client side cookies. You can set a cookie by creating a new cookie
object, setting the required fields, and finally calling setCookie on the HTTPResponse.
$declare(Cookie cookie = new Cookie())
$do(cookie.setName("maka"))
$do(cookie.setValue("shlaka"))
$do(cookie.setVersion("1"))
$declare(HTTPResponse res = getResponse())
$do(res.setCookie(cookie))
| |
Once set, you may retrieve a cookie by name on subsequent pages views by calling the getCookie() method on
the HTTPRequest object, or you can enumerate through all the cookies the client submitted via the getCookies()
method
$declare(Enumeration cookies = getRequest().getCookies())
$while(cookies.hasNext())
$declare(Cookie cur = <Cookie>cookies.next())
<li>Got cookie: $(cur.toString())
$endwhile
| |
With a cookie in hand you can inspect any of it's relevant fields (though only version,path and domain
may get set when a cookie is transmitted from the browser back to the server
<pre>
Cookie Name : $(cookie.getName())
Cookie Value : $(cookie.getValue())
Cookie Domain : $(cookie.getDomain())
Cookie Version : $(cookie.getVersion())
Cookie Path : $(cookie.getPath())
Cookie Comment : $(cookie.getComment())
Is Cookie Secure : $(cookie.isSecure())
Cookie : $(cookie.toString())
</pre>
| |
When users submit a form with enctype="multipart/form-data" they are actually submitting a multipart mime message
to the webserver.
<form method=post enctype="multipart/form-data">
File: <input type="file" name="uploaded_file" size="30" /><br>
<input type="submit">
</form>
| |
By filling the form input field with type=file, a user can upload a file to a webserver. This file may then be
parsed out of the HTTPRequest on the next page. It's contents will be maide available to the moto page in a
MIMEPart named with whatever name the form field was given.
$if(getRequest().getMIMEPart("uploaded_file") != null &&
getRequest().getMIMEPart("uploaded_file").getBodyLength() > 0)
$do(
putFileBytes(
"/usr/local/wa/dhakim/httpd/htdocs/foo.gif",
getRequest().getMIMEPart("uploaded_file").getBody(),
getRequest().getMIMEPart("uploaded_file").getBodyLength()
)
)
$endif
| |
It's possible to present users with multiple file upload or other form fields. These will all be submitted as MIME
parts. You can iterate over all submitted mime parts using the getMIMEParts() method of HTTPRequest.
You can also inspect the mime headers associated with each part.
Parts Sent:
<ul>
$declare(Enumeration parts = getRequest().getMIMEParts())
$while(parts.hasNext())
$declare(MIMEEntity curPart = <MIMEEntity>parts.next())
<li>$(curPart.getName())
<ul>
<li><b>filename</b> : $(curPart.getFileName())
<li><b>content type</b> : $(curPart.getContentType())
<li><b>content transfer encoding</b> : $(curPart.getContentTransferEncoding())
<li><b>body length</b> : $(curPart.getBodyLength())
</ul>
$endwhile
</ul>
| |
Regular forms (forms without the enctype set to multipart/form-data) do not submit their contents as multipart
mime messages. An explanation of how values sent through those forms are retrieved is dealt with in the following section
on state management.
State management in moto allows for
persistence of information in the
Context,
Sessions and
States.
The Context and the Session store <Object>s. The Context stores objects
that are global, may be set, updated and retrieved from any page, and never expire unless
explicitly removed from the Context and deleted. The Session
stores objects that are needed only for one user's Session and expire when
that user's Session expires. The State stores variables passed in
the URL or submitted via HTML form submissions (HTTP Post operations).
Before you can make use of moto's state management facilities however, you
must $use("codex.http")
State variables, also called CGI variables, are created by following a URL,
submitting a HTML form, or HTTP redirection. Following the URL
http://www.webcodex.com/index.moto?foo=bar&maka=shlaka
| |
Will add two variables to the state when the index.moto page is executed.
The state variable 'foo' will have the value 'bar' and the state variable 'maka' will
have the value 'shlaka'.
<form action="index.moto">
<input type="hidden" name="foo" value="bar">
<input type="hidden" name="maka" value="shlaka">
<input type="submit" value="click me">
</form>
| |
Clicking the 'click me' button on this form will make the same two state variables
with the same two values available to index.moto when it is executing.
$do(sendRedirect("index.moto?foo=bar&maka=shlaka"))
| |
Executing a moto page that redirects to another moto page via the above redirect will
also result in our two state variable being added.
The values of state variables can be retrieved within the code for index.moto in
one of three ways. First, the current state can be retrieved and a method can be
called on it to get the value of the state variable
$(getState().getValue("foo"))
| |
Second, if you want the value of a state variable for the current state (as you
usually do) than you can call the convenience function getValue() directly
Third, you can use the two argument version of getValue() to provide a default value
if the state variable is not defined in the current state.
There are usually multiple means of getting to a moto page some of which pass a
certain state variable and some of which don't. If someone got to index.moto
by typing
http://www.webcodex.com/index.moto
| |
directly into their browser for instance the
variable 'foo' would not be defined. In this case the getValue function (or method)
would return the value null. There are two ways in which to handle this case
In web application programming, state variables (URL or form variables) are
often looked at as arguments to the dynamic web page. If the argument is a
required argument then you would want it's non-existence to be treated as an error
$if(getValue("foo") == null)
$(sendRedirect(some error page)
$endif
| |
But if it was an optional argument than you will likely have some default value in
mind. In this case the third method of retrieving state variables is useful
In this case if the state variable foo was not passed than the default value 'bar'
will be used in it's place.
The characters =, &, and ? separate variable names from variable values,
variable name value pairs from each other, and the query string from the hostname
and path. In the case a variable name or value contains these special characters
(or spaces, or another special character '%') that name or value must first be URL
encoded before it may used in a URL.
<a href="index.moto?foo=$(urlEncode(foovalue))">
| |
The function urlEncode() will replace the special characters with their hexadecimal
counterparts %hex value which will be understood by the web server and
decoded on the next page view. Thus there is no need to URL decode explicitly in
your source code.
State variable values may also be set through user input in HTML forms
<form name="login form" method="post" action="do_login.moto">
User: <input type="input" name="user">
Password: <input type="password" name="password">
<input type="submit" name="_action" value="login">
</form>
| |
Clicking the login button on this form will actually submit three variables.
The state variable 'user' will contain whatever the user entered for the user prompt.
The state variable 'password' will contain whatever the user entered for his or her
password. There will also be a state variable '_action' which contains the value
'login'. Now let's suppose that the page do_login is coded as follows
$switch(getValue('_action',"login"))
$case(login)
$if(getValue("user","") eq "bob" && getValue("password","") eq "bobpw")
$do(redirect("index.moto?sid="+getState().getSID()))
$else
$do(redirect("login.moto?sid="+getState().getSID()+"&_err=Password+rejected"))
$endif
$endswitch
| |
Here we see that if the user attempted to log in with user name 'bob' and password
'bobpw' then he will be forwarded on to index.moto, otherwise he will be redirected
back to the login page.
For applications that make use of Sessions, the current Session is retrieved from the state
by calling the method getSession()
$declare(State s = getState())
$declare(Session session = s.getSession())
| |
Once you have the Session, you may retrieve named Session variables from it by calling the
get() method
$declare(Integer idUser = <Integer>session.get("idUser"))
$declare(String username = <String>session.get("uname"))
| |
Before you may get any objects out of the Session however, you must put objects into it. This
is done by calling two methods on the Session object: put() and promote().
$do(session.put("idUser",session.promote(new Integer(rset.getInt(0)))))
$do(session.put("uname",session.promote(rset.getString(1))))
| |
The put method does nothing more than associate a String valued key with the object you want to
store in the Session. The action of the promote method is less obvious.
All objects allocated in moto have an implicitly specified lifetime after which the storage
for the object will be recovered by the memory manager. By default the lifetime of an object ends
when the page that it was constructed on is done executing. These objects are said to have a page
lifetime. Objects that we want to make available on subsequent page views (without reconstructing
them) need to stick around longer than that. This is what the promote method does
When you call Session.promote() on an object you change its lifetime to the lifetime of the Session
you are storing it in. In the following section you will find that there is a Context.promote()
function as well. If promote() were not called on an object before it was put in the Session or
Context then the reference stored in the Session or Context would be invalid since the object may
have already been deleted.
Methods such as Session.put() do not implicitly call promote because there would be many cases it
could not catch. For instance suppose a SymbolTable was allocated and stored in the Session. Then
objects were put into the SymbolTable. In this case the reference for the SymbolTable may be valid
on subsequent page views, but the references for objects in the SymbolTable would not be. The bottom
line is anything that is put into anything that is put into anything that is put into the Session
or Context must be promoted.
The Context is the appropriate place to store
singletons, that is Objects that you only need
one of for your application. This is because there is only one Context and every page can get
objects out of or store objects in the Context. Perhaps the most common type of object to store in
the Context are caches.
To store an object in the Context, the Context must first be retrieved by calling the function
getContext(), the object to be stored should then be promoted by calling Context.promote(), and
finally the object must be associated with a name in the Context by calling Context.put().
$do(getContext().promote(cardData))
$do(getContext().put("CardList",cardData))
| |
To retrieve an object from the Context use the method Context.get()
cardData =<TabularData>getContext().get("CardList")
| |
There are a number of important factors to consider when evaluating what sort of
state management to use for your application. Will the application need to be
distributed, or does it require bookmarkable URLs ?
Will there be multi-page interview processes ?
Can you require your users to have client side cookies turned on ? Moto provides
built in support for four state management systems :
Stateless : A stateless application requires no information to be
maintained in a user's session from page view to page view. This would be the case for static
web sites or web sites whose content does not change based upon who is viewing it.
Client-Side : An application that requires knowledge only of who is
logged in in order to display information is a good candidate for client side
state management. When a user connects to the application a client
side cookie is deposited on the user's browser identifying the Session. Subsequent page executions
may retrieve information from the Session identified by the cookie.
Client side state management is preferable for bookmarkable applications but
may suffer from time travel problems and may not be used by browsers that have
disabled cookies.
Server-Side : Instead of setting a cookie on the user's browser to identify the
Session a state id is included in every link or form submission by using
a special state variable sid. The value of this variable changes from page view
to page view and identifies not only the Session but the previous State. Through this
mechanism state variables are
automatically persisted across page views without sufferring
from time travel problems. However, since the sid variable is included in every link
bookmarking pages of the application is not recommended as the Session may
have expired by the time the bookmark is used.
Hybrid Server-Side - Client-Side : In the hybrid system both a
client-side cookie and the sid variable are used. A correctly written application can default to
Server-Side state management if client-side cookies are turned off. Conversly, only application
areas that require the power of parent state identification such as multi-page interview
processes need to use the sid. Other bookmarkable pages will be able to identify the correct
Session via the cookie.
The type of State management your application uses is determined through a combination of
httpd.conf configuration options and coding practices used in writing the application. The
following diagram provides a graphical overview of Moto's state management subsystem.
To identify your application as
Stateless include the following configuration
option in your httpd.conf:
<Application Name>Option Session.Disable TRUE
| |
Or when running unter the interpreter:
<Location /~dhakim/docs>
MotoOption ExamplesPath /Users/dhakim/Workarea/content/moto/examples
MotoOption Session.Disable TRUE
</Location>
| |
What this will do is turn off session creation and tracking within the application (or
application area) where the directive applies.
Stateless is somewhat of misnomer because States are still created with every pageview.
What Stateless applications really lack are Sessions. Calling getSession() on the current
state will always return null
Server-Side state management is available to an application whenever Sessions are enabled.
Using it involves coding in such a way that a special State variable sid is passed
in every hypertext link, location redirect, and form submission.
Every time a user visits a page when Sessions are enabled Moto attempts to discover whether
this user already
has a Session. A Session in moto doesn't mean that the user is necessarily logged
in, just that the user has been interacting with the application on previous page
views and has browsed to this page by following links on those pages.
When Moto cannot determine the 'current Session' a new Session is created.
States are created on every page view. If Sessions are enabled then when a
State is created it is bound to the current Session. The way moto discovers the current Session
under Server-Side state management is by looking at a State variable named 'sid'.
If it is present in the current State then
Moto can derive the current Session from it and bind the newly created State to that Session.
The 'sid' variable's value identifies a State. It is used to identify the parent State
of the currently executing page. If it is not present then Moto assumes there was no
'parent State'
and spawns a new Session. Otherwise the parent State is retrieved and the Session it was bound
to identified. The newly created State is then bound to that same Session. The process
perpetuates itself when, on the currently executing page, the new State's own id
(returned by the method getSID()) is
included in dynamically generated links.
<a href="http://www.webcodex.com/index.moto?sid=$(getState().getSID())>
| |
location redirects
$(sendRedirect("index.moto?sid="+getState().getSID()+))
| |
and form submission by passing the sid as a hidden variable
<form action="index.moto" method="post">
<input type="hidden" name="sid" value="$(getState().getSID())>
<input type="submit" value="click me">
</form>
| |
When the link is followed, the form submitted, or the browser redirected, Moto
recognizes the sid, discovers the State it refers to, and from that state discovers
the Session.
This mechanism is used to provide continuity to the user experience not only through Session
identification but by persisting the values of State variable between page views.
State variables in the parent state that do not begin with an underscore '_' are copied
into the current state. So if the state variable 'foo'
was present in the parent state than it is present in the current state with the
same value. It's value changes only when a new value is passed for it. Thus in the login page
example from the previous section
<form name="login form" method="post" action="do_login.moto">
User: <input type="input" name="user">
Password: <input type="password" name="password">
<input type="submit" name="_action" value="login">
</form>
| |
$switch(getValue('_action',"login"))
$case(login)
$if(getValue("user","") eq "bob" && getValue("password","") eq "bobpw")
$do(redirect("index.moto?sid="+getState().getSID()))
$else
$do(redirect("login.moto?sid="+getState().getSID()+"&_err=Password+rejected"))
$endif
$endswitch
| |
The value of the State variable "user" could be retrieved on all subsequent page views. This
is handy because it does not require the extra work of inserting it into the Session or
promoting its value. There are further benefits which are discussed in detail in subsequent
sections.
Continuing from the previous example of login and do_login pages, a user has just entered a username (bob) and password on the login page, pressed submit taking
him to the do_login page where his account was validated and his browser redirected to an index
page.
If this were an application that made use of Server-Side state management then on the index
page we could write
Welcome $(getState("user"))
| |
And it will print
We could not however print
You clicked $(getState("_action"))
| |
which would result in
Since '_action' began with an underscore it's value is not copied to subsequent
states and thus available to subsequent page views.
Form submissions account for the way Moto handles State variables with names that
begin with '_'.
In general:
- Most values submitted in forms are only needed immediately after the form submission
- Values from form submissions may be quite large and as such expensive to copy from page view to
page view.
Consider a web application that is used for maintaining an employee directory. One
could imagine a page containing a form that solicits data for a new hire which,
upon submission, is inserted into a database. Let's suppose this form prompts for the
employee name, address, and a photo that can be uploaded. All this data will be
submitted to moto as state variables. The page that handles this submission will
insert it into the database and then return to the index where upon the user can
choose to add another employee or browse through other application pages. Either
way this data is unnecessary, and, with regard to the employee photo, potentially
quite large. We would definitely not want it copied with every page view.
When the Session.Disable option is present, all state variables are free'd after page execution.
In Stateless applications the variable sid is meaningless and not prefixing state variables with
an underscore has no effect. They all go away at the end of the page view to which they were
submitted.
But why copy state variables, why not just keep one value around for every state variable
name used in a user's session? The answer to this question requires some
background...
When building web applications it is useful to diagram them out in Application Flow
Diagrams. These
diagrams map out the pages of a web application with arrows between them
illustrating the links that a user could follow to transition from page to page. A simple
application where users may view or modify a 'user profile' might be diagrammed as follows:
A user's activities in a web application can be viewed as a traversal of
the transition diagram.
State |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
Parent State |
none |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
Page |
1 |
2 |
1 |
2 |
3 |
4 |
5 |
4 |
For the traversal above the user forgot his password the first time and was sent
back to the login page to start again. This is clear by comparing the pages the
user visited to the transitions in the app flow diagram.
The user then went on to the index page, viewed his profile, and successfully edited it.
But what could we conclude happened if we then saw
State |
9 |
10 |
11 |
12 |
Parent State |
1 |
9 |
10 |
8 |
Page |
1 |
2 |
3 |
5 |
The user backed up to the login page, openned up a new browser window and logged in as
(potentially) a different user in
it. Then went back to the original window and followed the edit profile link! If the only mechanism
being used to determine what user profile to edit was based upon an identifier tied to the Session
then the user whose profile will be edited in the first window will
be the user who is logged in in the second. Another way to look at this is that the actions the
user took by opening up a second window and logging in as a different user affected the
behavior of a link that was generated previously. This is known as a time travel problem.
Implications of this sort of problem are
- Using the back button on applications that make extensive use of Session objects
or (or Client-Side State management) may be unsafe.
- Following links by openning them up in new windows may effect the ability to follow links in
other open windows.
- Session objects are tricky things that must be used with care.
Use of Session objects is the most common cause of unexpected time travel problems. If the 'current user'
in the application diagrammed above was stored as a persistent State variable there would be no
confusion as to which 'user' was being edited in which window. If you believe that the above example
is somewhat contrived consider the role of an HR administrator who may be responsible for updating
the profiles of multiple employees ... this sort of thing happens all the time.
One area where this comes up a lot is in relation to multi page interview processes. In these sorts
of cases users are asked to enter information through a series of interactions with a web based
application. In general the information is persisted in memory until it is ready to be stored. An
example of this might be web page building application where fields of a web page template get filled
in over successive page views
State |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
Parent State |
none |
1 |
2 |
3 |
4 |
5 |
6 |
Page |
1 |
2 |
3 |
2 |
3 |
2 |
3 |
In the above traversal the user has selected a page to edit and is entering data into each of it's
fields
State |
8 |
9 |
10 |
Parent State |
1 |
7 |
9 |
Page |
2 |
2 |
1 |
It seems as though the user has gone back to the directory screen by using the back button on
his browser and selected a new page in a new browser window (perhaps to copy some information out
of that page into the page he's working on). Back in his working window he then alters the field and
then saves the page. But which page did he alter? If the data for a page is stored in a
Session variable named 'page' for instance then the very act of opening up a second page in another
window has undone all the users work.
So now that we are aware of the hazards of Session objects because of time travel problems, one might
ask why we should ever consider a State Management methodology where one is forced to use Sessions
to persist data between page views. The biggest reason may be bookmarkability. Including
the state variable sid in every URL means that bookmarking generated URLs may be useless or
potentially dangerous if the link is distributed to others.
With pure Client-Side state management a Cookie named ssid is deposited on a users browser after
his first page view against an application. This Cookie does nothing more than identifies the
Session. If on subsequent page views Moto retrieves this Cookie then Moto can discover the current Session.
If the cookie is not included within the HTTP GET or POST then Moto assumes there is no current Session
and spawns a new one.
You can get this behavior by including the option
<Application Name>Option Session.SetSSIDCookie TRUE
| |
Or if using the interpreter use the following moto option in the location you want it applied:
MotoOption Session.SetSSIDCookie TRUE
| |
The lifetime of the ssid cookie is the same as the lifetime of the Session which is specified in
seconds via the Moto option
MotoOption Session.Timeout 60
| |
The default value for this option is 1800.
The downsides of this system apart from the time travel phenomina we've already looked at are :
- Some users may have client side cookies disabled
- It becomes impossible to maintain multiple Sessions at once against the same application under
one browser (sometimes this makes testing difficult)
- State variables are not persisted meaning that all data for a users session must be promoted and stored
in the Session
The best State management systems for web applications are hybrid systems. They are also the most complex to
implement. The way a hybrid system is implemented in Moto is by including the Session.SetSSIDCookie option in
your httpd.conf file AND including the sid variable on all pages that could benefit from it and do not need to
be bookmarked OR in every generated URL in cases where client side cookies are turned off.
You can discover whether cookies are working or not by trying to getCookie("ssid") on a page that expects it.
If it is not found, include the State variable sid on all generated URLs.
Simply by enabling the SetSSIDCookie option on applications that are basically Server-Side State managed
adds an extra level of security. If the Session identified by the sid and the Session identified by the ssid do
not match then a new session is spawned.
Like most web programming languages, the moto language was designed with database interaction
in mind. Extensions are included with moto in order to facilitate interaction with Postgres and
MySQL databases. To connect to one of these databases you must use the appropriate codex.db library
$use("codex.db.mysql")
$use("codex.db.pgsql")
| |
Prior to using a database you must connect to it, this is done in moto by constructing a connection object
$declare(MySQLConnection myconn = new MySQLConnection(server address,database name,user,password))
$declare(PGConnection pgconn = new PGConnection(server address,database name,user,password))
| |
Once you have a connection to the database you can issue SQL queries against it.
Queries results are returned in a ResultSet.
$declare(String query =
"SELECT cardsets.id,cardsets.name,cardsets.shortname,count(print.id) AS cardcount ,sum(price.buy) AS setprice,sum(price.diff) FROM cardsets,print,price WHEREprice.print=print.id AND print.edition=cardsets.id GROUP BY print.edition")
$switch(getValue("_orderby","Set"))
$case("Set")
$do(query = query + " ORDER BY cardsets.name")
$case("Cards")
$do(query = query + " ORDER BY cardcount DESC")
$case("Price*")
$do(query = query + " ORDER BY setprice DESC")
$endswitch
$declare(MySQLResultSet rset = conn.query(query))
| |
Those results are then iterated over and either displayed or used in other computations. The result set object
keeps track of what row you are currently looking at. Methods are called on the result set object to retrieve
individual columns of the current row.
$while( rset.next())
<tr>
<td align=center><img src="images/exp_sym_$(rset.getString(2)).gif"></td> <td >
<a href="cardlist.moto?sid=$(
getState().getSID()
)&searchby=set&set=$(
rset.getString(0)
)">$(rset.getString(1))</a>
</td> <td align=center >
$(rset.getInt(3))
</td> <td align=center ><nobr>
$("$ ")$(rset.getString(4))
$if(rset.getFloat(5) > 0)
<img src="images/uparrow1.gif">
$elseif(rset.getFloat(5) < 0)
<img src="images/downarrow2.gif">
$endif
</nobr></td>
</tr>
$endwhile
| |
The result set method next() must be called prior to retrieving values for fields in the result set. This method
returns true if there are more rows in the result set then scrolls to the next one.
Values for specific columns in the result set are returned either by specifying the column number
(as in the above example) or by referencing the column name. Column names are not case sensitive.
$(rset.getString("column name"))
| |
To find out how many rows were returned in the result set, call the method size() on the result set object
You can also execute SQL inserts, updates, or deletes against the database.
This occurs via the update method of the Connection object. This method returns the
number of rows inserted, updated, or deleted by the method.
$if(rset.next())
$do(deckid=rset.getInt(0))
$do(conn.update("DELETE FROM deckcards WHERE deck="+str(deckid)))
$else
$do(conn.update(
"INSERT INTO deck VALUES (0, '"+
conn.escape(getValue("_name"))+"','"+
conn.escape(getValue("_creator"))+"',"+
str(uid)+",0)"
))
$do(deckid = conn.insertID())
$endif
$do(tok= new Tokenizer(ids,'|'))
$while((curID=tok.next()) != null)
$declare(String curQTY =getValue("_q"+curID))
$if(curQTY != null && curQTY ne "0")
$do(conn.update(
"INSERT INTO deckcards VALUES ("+str(deckid)+","+curID+","+curQTY+",0)"))
$endif
$do(curQTY =getValue("_s"+curID))
$if(curQTY != null && curQTY ne "0")
$do(conn.update(
"INSERT INTO deckcards VALUES ("+str(deckid)+","+curID+","+curQTY+",1)"))
$endif
$endwhile
| |
When inserting String values into the database you should first escape the string value being inserted.
This is accomplished by calling the connection method escape() on the value to be inserted. Binary
data may also be escaped in this way for insertion into BLOB fields.
The escape function effectively backslashes single quotes and other restricted characters.
conn.escape(String value to be escaped)
conn.escape(byte[] to be escaped)
| |
Once connected to a database you can retrieve a list of tables in the database as a result set by
calling the listTables() method on your connection.
$do(rset=conn.listTables())
$while(rset.next())
$(rset.getString(0))
$endwhile
| |
Once you have a result set in hand you can retrieve the names and types of the columns returned
$declare(int cols=rset.getColumns())
$declare(int i)
$for(i=0;i<cols;i++)
$(rset.getColumnName(i))
$(rset.getColumnType(i))
$endfor
| |
ResultSets cannot be stored in the session or the context since the results
are not stored in shared memory. Yet caching queries often provides a very good performance
benefit. To do this, TabularData objects have been provided to store result sets in shared memory.
The TabularData object is provided in the codex.util extension.
$use("codex.util")
$declare(TabularData ocs)
$if((ocs=<TabularData>getContext().get(query)) == null)
$do(ocs = conn.query(query).store())
$do(getContext().promote(ocs))
$do(getContext().put(query,ocs))
$endif
| |
TabularData objects are similar to result sets except the entire result is stored in
memory. Thus rows my be accessed in an arbitrary order.
$for(i=0 ;i < ocs.getRows() ;i++)
$do(curSet = ocs.getInt(i,0))
$endfor
| |
The standard field retrieval functions getInt,getString,getFloat etc... take both a row and a column (
or a row and a column name)
TabularData objects are also modifyable and may be used as a convenient data structure with which to
store arbitrary table data
$declare(TabularData cardData)
$if((cardData =<TabularData>getContext().get("CardList")) == null)
$declare(MySQLResultSet cacherset = conn.query("SELECT max(id) from print"))
$do(cacherset.next())
$do(cardData = new TabularData(
cacherset.getInt(0)+1,
10,
"shortname,name,card,print,color,type,cost,rarity,price,diff",
"String,String,int,int,char,String,String,char,float,float")
)
$do(cacherset = conn.query(
"SELECT cardsets.shortname,card.name,card.id,print.id" +
",card.color,card.type,card.cost,card.rarity,price.buy,price.diff "+
"FROM cardsets,card,print LEFT OUTER JOIN price "+
"ON price.print=print.id "+
"WHERE card.id = print.card "+
"AND print.edition = cardsets.id"
))
$while(cacherset.next())
$do(cardData.setString(cacherset.getInt(3),0,cacherset.getString(0)))
$do(cardData.setString(cacherset.getInt(3),1,cacherset.getString(1)))
$do(cardData.setInt(cacherset.getInt(3),2,cacherset.getInt(2)))
$do(cardData.setInt(cacherset.getInt(3),3,cacherset.getInt(3)))
$do(cardData.setChar(cacherset.getInt(3),4,cacherset.getChar(4)))
$do(cardData.setString(cacherset.getInt(3),5,cacherset.getString(5)))
$do(cardData.setString(cacherset.getInt(3),6,cacherset.getString(6)))
$do(cardData.setChar(cacherset.getInt(3),7,cacherset.getChar(7)))
$do(cardData.setFloat(cacherset.getInt(3),8,cacherset.getFloat(8)))
$do(cardData.setFloat(cacherset.getInt(3),9,cacherset.getFloat(9)))
$endwhile
$do(getContext().promote(cardData))
$do(getContext().put("CardList",cardData))
$endif
| |
The moto language, like most web programming languages, was designed primarily for webmasters with some technical ability
to intersperse programmatic constructs within web pages to create web applications. Moto was built with performance in mind
because the history of web programming languages such as Coldfusion, ASP, and PHP, shows that they are used more often than
not as the entire platform for rapid web application development. Nonetheless, the preferred way of using moto is for the
user interface and form handling aspects of your web applications alone. Business logic, where possible, should be
implemented in a classical programming language such as C.
To this end, an API has been provided for the creation of new moto objects and functions from your own C libraries.
Moto extensions are organized into libraries that can be used in your moto pages via the $use construct. These libraries
reside under the mx directory of your moto home directory. The structure of the mx directory is loosly modelled after a java
class library structure. It is heirarchical with the top level subdirectories conventionally named after the source company
that maintains the moto extensions within. Underneath each top level directory there may be further subdirectories
dividing the contained extensions into frameworks by function. Entensions are made available to the a moto page one folder
at a time.
To add a new extension library you should begin by creating a new directory underneath the mx directory. You should then copy
the makefile file from one of the existing extensions folders into your new folder. You can now begin creating interface files that map extension functions and object methods to C functions in your own C libraries.
For each new object or set of related functions you wish to expose to your moto application, you should create one interface
file (.i) . The convention is that this interface file be given the same name as the extension which it contains and thus
start with a capital letter. Interface files have the following general structure:
Extension: 1st Extension / Class Name
LibraryPath: 1st library path needed to locate shared libs this extension depends upon
LibraryPath: 2nd library path needed to locate shared libs this extension depends upon
...
IncludePath: 1st path to header files needed to compile this extension
IncludePath: 2nd path to header files needed to compile this extension
...
Library: 1st shared library file needed for this extension
Library: 2nd shared library file needed for this extension
...
Archive: 1st archive needed for this extension
Archive: 2nd archive needed for this extension
...
Include: 1st C header file needed to compile this extension
Include: 2nd C header file needed to compile this extension
...
Interface:
prototype mapping ;
prototype mapping ;
prototype mapping ;
...
| |
The prototype mapping is where moto functions and methods get translated to their C counterparts. A prototype mapping
begins with the return type of the moto function or method preceded by the keyword tracked if the return value
is an object created on the heap by the function or method call. If the function or method returns nothing then
void should be specified as it's return value. Following the return type is the function or method name. In the
case of a method name, the C++ style qualified name should be used
After the method name there is an open paren '(' followed by a comma delimeted list of argument type argument name pairs
followed by a close paren ')'. After the argument list comes the mapping symbol => and finally the C function
prototype. A complete example of an interface file taken from the codex.util library for the SymbolTable class follows:
Extension: SymbolTable
Include: "symboltable.h"
Include: "enumeration.h"
Interface:
tracked SymbolTable SymbolTable::SymbolTable() =>
SymbolTable *stab_createDefault();
void SymbolTable::~SymbolTable() =>
void *stab_free(SymbolTable *this);
void SymbolTable::put(String key, Object value) =>
void stab_put(SymbolTable *this, char *key, void *value);
Object SymbolTable::get(String key) =>
void *stab_get(SymbolTable *this, char *key);
void SymbolTable::remove(String key) =>
void *stab_remove(SymbolTable *this, char *key);
void SymbolTable::clear() =>
void *stab_clear(SymbolTable *this);
tracked Enumeration SymbolTable::keys() =>
Enumeration *stab_getKeys(SymbolTable *this);
int SymbolTable::size() =>
int stab_size(SymbolTable *table);
| |
There are three important times when the dependencies for an extension must be known.
- When the extension is compiled with mxc into .a and .so libraries
- When the extension is dynamically loaded by the moto interpreter
- When the extension is linked to a binary or apache module with mmc
At compilation time when the .a and .so files are created, header files that declare C prototypes
used in .i files must be listed using the Include keyword so that they can be included when building extension object
files. If these headers aren't in a standard location such as /usr/include the path to the
directory containing these header files must also be specified using the IncludePath keyword
so that mxc and mmc know where to look for them.
Using IncludePath to add the path to the moto include directory is not necessary as it is automatically
included by mxc and mmc.
The Library keyword in an interface file allows you to specify that your extension depends on a third party
library. This keyword should only be used for linking to shared libraries. It will generate a runtime dependency from the
extension (and therefore from modules compiled with that extension) to that library.
Libraries you should never link to via the library keyword include libmotom and libcodex. All the symbols
from these libraries are included in the command line interpreter and in mod_moto and are automatically linked
by mmc. Linking to these libraries will cause duplicate symbol errors on some platforms.
If the shared library you depend on is not in a standard location such as /usr/lib then you must specify the path
to the directory containing the shared library by using the LibraryPath keyword.
Often times it is preferable to actually copy the necessary symbols out of a library directly into an extension.
This is the case when you do not wish to have the runtime dependency on that library. Specifying a full path
to an archive that contains the necessary symbols (a .a file) using the Archive keyword will tell both mxc and mmc
to statically link to that archive (copy out symbols).
Sometimes archives and shared libraries may themselved be dependant on symbols in other libraries. These dependencies
should also be listed with the Library or Archive keywords.
Often times there will be a choice to link to either a shared library or an archive.
The general rule should be to link to common shared libraries that are guarenteed to be present in the same location
on platforms where binaries compiled with your extension will be deployed.
If you cannot guarentee that, then statically link to an archive if possible.
This diagram shows the current design of the libraries that are distributed with moto.
Always include "memsw.h" this is very important because this header redefines malloc, realloc, and free to
allocate memory on the shared heap. If your methods in your extension return Objects (a constructor for
example) than that memory needs to be allocated on the shared heap. If it is allocated with the plain
old system malloc than that memory will leak and never get cleaned up. Worse, your object won't be promotable.
That is, moto programmers won't be able to persist it in the session or context and if they try to, Apache will
likely segfault when they next try to access it. Bottom line ... include memsw.h .
Usually include "excpfn.h" and "exception.h". With these you will be able to throw and catch exceptions using the
macros TRY, CATCH, CATCH_ALL and END_TRY. This is much better than segfaulting (or returning error codes ... yuck)
when something goes wrong inside your extension. Moreover excpfn.h defines a number of usefull function that mimic
their C stdlib counterparts but allocate memory on the shared heap and throw exceptions when the arguments passed
to them are bad. These functions include emalloc, erealloc, estrdup, and estrcmp.
Always create a destructor. Without a destructor moto will just call plain old free on your object and if you
have any member objects those won't get cleaned up.
Copyright © 2000 - 2003 David Hakim