Language Guide
================
Magro is a language with very few syntax elements and most of the
functionality will be determined by user defined macros and libraries.
An example script and its output is show below::
import 'html5'
html:
body:
div(id='d1'):
'Some text'
Will generate the following output::
Some text
Macros
------
Macros are the basic unit of the Magro language, they can be
defined and called with a set of parameters and an optional body.
The result of the call will be appended to the output text. If a
call to a macro cannot be resolved, it will simply be ignored.
The name of any macro can be any succession of alphabetic or
numeric characters, underscores ('_'), dots ('.') or hyphens ('-')
Invocation of macros
~~~~~~~~~~~~~~~~~~~~
A macro can be called without parameters::
this_is_a_macro_call
With parameters::
macro( 'parameter' )
With a body parameter, indented from the parent call::
macro:
'body'
anothermacro( 'parameter' ):
body:
anothercall
'more body'
In the last example, the macro call ``anothercall`` is the body
of the call to the ``body`` macro. The body of the ``anothermacro``
call is composed of the call to ``body`` and the ``'more body'`` string.
In magro, indentation is important as it is used to determine what
expressions belong to the body of each call. Note that when a body
is used in a macro call, a colon ``':'`` must be written to make it more
explicit that this macro call has a body.
Parameters
..........
The list of parameters is optional for the call and has the
following syntax::
( parameter_name = parametervalue, param2 = value2, paramN = valueN )
The parameter name is optional and the parameter value can any sequence
of expressions separated by spaces, either of strings, of macro calls,
cycles or any combination of them; for example::
div( id='head' index, class='container' ):
'body of the div'
In the example above, the parameter `` will be assigned a value formed by
the concatenation of ``'head'`` and the result of the call to ``index``.
Evaluation order
................
A function call will try to determine its value in the following order:
1. Look up the value in the context the user passes to the parser at invocation.
a) Look up a renderer for the type of the found object.
b) Use the string representation of the found object.
2. Look up the value as a parameter passed to the called macro.
3. Look up a macro definition of the same name.
4. Use an empty string as result.
Definition of macros
~~~~~~~~~~~~~~~~~~~~
A macro can be defined using the following syntax::
def macroname( parameters ):
code_of_the_macro
The ``code_of_the_macro`` is a sequence of calls of the form described in the
previous section, that will be invoqued when the defined macro is called.
The list of parameters is optional and can be used to define default values to
any parameter that the used might ommit from the macro call. At the moment of
invocation, the caller might decide not to use all of the declared parameter names
or to pass any parameter that is not declared in the macro definition. For example::
some_macro( some_random_param='some_value' )
def some_macro( id, class='default' ):
'Macro value'
In this case, ``id``will have no value, ``class`` will have the string ``'default'`` as
value, and some_random_param will be passed to the call even if it is not used by the
macro definition.
A call to a parameter is actually the same as a macro call, and the rules for determining
its value described in the previous section apply to it.
Implicit values
~~~~~~~~~~~~~~~
There are a number of symbols that can be used on macro definitions without being explicitly
declared or given values by its caller. All these values have the following form::
$name_of_variable
If the implicit variable does not exist, it will be ignored.
All macro declarations can use the following implicit values:
``$``
Represents the body used in the macro call. If no body was used in the call, it is an empty string.
``$all``
Returns an array of all the parameters used during the call in the order they were written.
``$undeclared``
Returns an array with all the parameters used at the macro call, but only if they are not declared
in the macro definition.
``$1, $2, ...``
Besides the actual names of the parameters (in case their names were used,) each parameter can be
accessed by its position in the macro call, where ``$1`` is the first parameter, ``$2`` the second
and so.
More implicit values will be explained in the context they are available.
Type renderers
~~~~~~~~~~~~~~
Special macro declarations can be defined to be invoqued when an object of certain type is encountered.
This kind of macros can be defined using the following syntax::
@name_of_the_type( parameters ):
code_of_the_macro
The engine will try to match the object type hierarchy with the name used in the macro definition. If no
definition is found, the string representation of the object will be used.
The ``code_of_the_macro`` section and parameters definition follows the same rules described in section 4.1.2.
Except for the fact of being invoqued when a type match is found, this kind of macros behave just like normal
macros.
Implicit values
...............
There is an additional implicit value that will be available in this kind of macros:
``$object``
Contains the reference to the object that triggered the macro invocation.
Strings
-------
Strings can be used inside macro calls or as part of parameter value definition. They can be written
in three diferent forms:
1. **Normal strings**, written between single (') or double quotes (") with no new line inside them::
'This is a normal string'
"This is a normal string too"
2. **Line strings**, beginning with a single or double quote and ending with a new line::
'This is a line string.
'Line strings will have a new line
'appended at the end.
"This is another line string.
3. **Long strings**, written between a pair of three single quotes (''') or double quotes (""") can contain new lines inside them::
'''This is a
long string
of many
lines'''
"""This is another
example of a
long string"""
All kinds of strings can contain escape characters using the same set as Python strings. Please refer to the Python
documentation for more details on this.
Cycles
------
As many template engines, Magro has a special syntax to support loops over a collection of
items, a set of parameters or an iterator. The syntax for a loop is as follows::
[lists_of_values]:
expressions_evaluated_for_each_value
Each part has its own rules described below.
The list of values
~~~~~~~~~~~~~~~~~~
Syntax example::
[ optional_key = value, key2 = value2, another_value ]
This is a list of values separated by commas (,) very similar to the parameters passed to a
macro call, but with the following exceptions:
1. Values that evaluate to an array, will be expanded and iterated
as if they were single values. For example::
[ $all ]: $value
Will iterate over all the parameters passed to the macro call, represented
by the implicit variable ``$all`` that evaluates to an array containing all
the parameters that were used during the macro call.
2. Values that evaluate to an empty string and don't have a key will be
ignored from the loop. There is a way to define an alternative
body block by using a single colon (:) at the same level of indentation right
after the main block of the loop. This alternate block will be called for
those values of the loop that evaluate to an empty string. This feature
can be used to implement a simple conditional expression::
[ condition ]:
'This will be printed if condition does not evaluate to empty'
:
'This will be printed if condition evaluates to empty'
Groups
......
Arrays expansion can be prevented by enclosing in parentesis the variables
that evaluate to an array::
[ firstarray=($all), secondarray=('1','2','3') ]:
[ $value ]:
'A nested loop'
As shown in the example, a set of comma separated expressions can be grouped
in an array by enclosing them between parentesis. This applies to parameters
passed to a macro call too.
The expressions to evaluate
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The body of loop expressions is very similar to the body of a macro call,
except that it cannot contain macro calls that contain a body. This limitation
might change in a future version of the language.
There are some implicit variable names available only inside loop expressions:
Implicit values
...............
``$key``
Returns the value of the key used for the value currently evaluated.
If no key was used, this variable will be ignored.
``$value``
Returns the current value of the iteration.
``$index``
Returns the position of the current value of the iteration, starting
from 0.
Import clause
-------------
Macro definitions declared in a separate file can be used by importing them
using the import clause::
import 'file_name'
The file to import will be searched in order using all the paths declared in the
``magro.env.path`` variable.
Only the macro definitions will be imported, and all output generated by calls made
inside the imported file will be ignored.
Python expressions
------------------
Python expressions can be used inside magro templates by enclosing them inside
back-quotes (`). For example::
`range( 1, 10 )`
The result of the expression can be of any type, and can be used as a parameter
value, as a single expression, or as part of the body of a macro call.
If a certain module needs to be imported for its use in python expressions, it can
be done by enclosing a python import sentence inside back-quotes. For example::
`from random import random`
`random()`
Values passed to the template and parameters used in a macro call are available
as variables inside the Python expression. For example::
`from random import random`
randomtext( 'abcde', `10` )
def randomtext( seed, size ):
`''.join( [ seed[int(random()*len(seed))] for i in range(size)] )`
If the variable name is an implicit value or its name has a format incompatible
with python syntax for variable names, the function ``_( varname )`` can be used
to retrieve such value::
[ '1','2','3' ]: ' value*10=' `int( _('$value') )*10`
It is recommended that Python expressions used inside templates have no collateral
effects as that templates should be used only for output presentation.
The Context object
------------------
A template can be passed a ``dict`` object to define the values of the symbols used by it.
Evaluation of the names used in templates is done as detailed in section 4.1.1.
As sometimes the value of a macro call will evaluate to a Python object, it would be
convenient to be able to access the object's attributes from inside the template. This
could be done using a Python expression, but a better approach is supported by using
the ``magro.context.Context`` class.
The ``Context`` is an extension to the ``dict`` type that will follow the next sequence
when searching for a key contains dots ('.') inside it :
1. Search for the whole key in the dict.
2. Ignore the last part of the key (after the last '.').
3. Repeat steps 1 and 2 for each new key until an object is found
or the key has no more parts.
4. If an object is found, try to evaluate all the attributes in
the sequence.
For example::
>>> from magro import Template
>>> from magro.context import Context
>>> template = Template( 'a.imag "i"' )
>>> template.render( Context({'a': 10+5j}) )
u'5.0i'
When an referenced attribute of an object is callable, it will be called without arguments
and the returned value will be used as the attribute's value.
If no object is found a KeyError is raised. When an object is found but the sequence of
attribute references is not possible, an AttributeError will be raised.
Comments
--------
Commentaries to the code can be written beginning the comment with a ``'#'`` character. All text
following this character up to the next new line character will be ignored.