Skip to content

Commit

Permalink
sample and documentation updates, bug fixes
Browse files Browse the repository at this point in the history
imported from personal svn:
* added cpp reflection sample
* added nest indentation sample
* added $< line slurp
* added utils module
* rst documentation updates
  • Loading branch information
kevinic committed Apr 11, 2013
1 parent e6748ed commit 1112a5c
Show file tree
Hide file tree
Showing 23 changed files with 897 additions and 65 deletions.
3 changes: 3 additions & 0 deletions sphinx/source/api.rst
Expand Up @@ -9,3 +9,6 @@ Python API
.. automodule:: texthon.parser
:members:

.. _utility_module:
.. automodule:: texthon.utils
:members:
99 changes: 71 additions & 28 deletions sphinx/source/documentation.rst
Expand Up @@ -56,22 +56,25 @@ contains sample templates. They are:
* `hello <https://github.com/kevinic/texthon/tree/master/tests/hello>`_ - a minimal example
* `basic <http://github.com/kevinic/texthon/tree/master/tests/basic>`_ - shows basic directive use and template module loads
* `html <http://github.com/kevinic/texthon/tree/master/tests/html>`_ - shows the use of template mixins and parser parameters
* `nest <http://github.com/kevinic/texthon/tree/master/tests/nest>`_ - using the :ref:`utility class <utility_classes>` to control indentation
* `cpp <http://github.com/kevinic/texthon/tree/master/tests/cpp>`_ - templates that generate c++ structs and type information

Directives
============

.. highlight:: none

.. productionlist::
directive: dir_prefix dir_command dir_statement
dir_command: "`* <comment_directive>`" | "!" | "{" | "}" | dir_keyword
dir_keyword: "load" | "import" | "attribute" | "template" | "end"
directive: <dir_prefix> dir_statement
dir_statement: `comment` | exec_stmt | keyword_stmt
exec_stmt: `single_exec` | `compound_begin` | `compound_end`
keyword_stmt: `load_stmt` | `import_stmt` | `attribute_stmt` | `template_stmt` | `end_stmt`

Directives are Texthon control commands and do not affect the output text
directly. A directive is issued on its own line, beginning with the directive
prefix token. The rest of the documentation assumes '#' is the prefix.

Prepend '\' to escape the token. Escapes are only processed at the beginning
Prepend '\\' to escape the token. Escapes are only processed at the beginning
of the line.

Directives operate either in the module scope or the function scope. Only
Expand Down Expand Up @@ -99,9 +102,10 @@ lines.
Comment
-----------

Module and function scope::
Module and function scope:

#* <comment>
.. productionlist::
comment: "*" <comment>

Comments are ignored at the module scope, and emitted as Python #
comments at the function scope to aid debugging.
Expand All @@ -115,9 +119,10 @@ Example::
Import
-----------

Module scope::
Module scope:

#import <module> as <alias>
.. productionlist::
import_stmt: "import" <module> "as" <alias>

Import a Python module under <alias>. The alias is required. Once imported,
the alias will be available for all definitions in the module.
Expand All @@ -132,9 +137,10 @@ Example::
Load
------------

Module scope::
Module scope:

#load <path> as <alias> [(attributes)]
.. productionlist::
load_stmt: "load" <path> "as" <alias> ["(" <attributes> ")"]

Load another template module in the file specified by <alias>. <path> is
resolved relative to the directory of the current file and the list of
Expand All @@ -153,17 +159,18 @@ following attributes are available:
Example::

#load "lib/file.tmpl" as lib
#load "lib/file.tmpl.html" as html_lib (directive_token = <!--)
#load "lib/file.tmpl.html" as html_lib (directive_token = "<!--")
#load "lib/file.tmpl" as lib (abs)

.. _attribute_directive:

Attribute
----------------

Module scope::
Module scope:

#attribute <name> <expression>
.. productionlist::
attribute_stmt: "attribute" <name> "=" <expression>

Declare a module level variable <name> and assign the result of <expression>.

Expand All @@ -185,10 +192,11 @@ Example::
Template Function
------------------

Module scope::
Module scope:

#template <name>[(parameters)]
#end template
.. productionlist::
template_stmt: "template" <name> ["(" <parameters> ")"]
end_stmt: "end" "template"

Define a template function within the current module. Anything between
``#template`` and ``#end template`` are evaluated in function scope.
Expand All @@ -213,9 +221,10 @@ Example::
Single Statement
-----------------

Function scope::
Function scope:

#! <python_statement>
.. productionlist::
single_exec: "!" <python_statement>

The entire <python_statement> is copied over to the generated template function
code (with indentation stripped). Write the statement as if it were inside a
Expand All @@ -236,10 +245,11 @@ For builtin variables available to the execution statement, refer
Compound Statement
-------------------

Function scope::
Function scope:

#{ <python_compound_statement>
#}
.. productionlist::
compound_begin: "{" <python_statement>
compound_end: "}"

Compound statements are useful for Python control logic such as ``if``,
``for``, and ``while``. Text lines and other execution statements between
Expand Down Expand Up @@ -315,32 +325,52 @@ For complex substituion, surround a Python expression with brackets.
<python_expression> will be evaluated using Python ``eval``, and the result
is used for the substitution.

'\' can be used within the expression to escape the ending bracket '}',
'\\' can be used within the expression to escape the ending bracket '}',

Example::

the sum of the values is ${a + b}
lookup result: ${values[key]}

Slurp Placeholder
Slurp Placeholders
---------------------

::

$<
$>

Two special place holders, ``$<`` and ``$>`` , are used to discard parts of the
text line; they are useful for whitespace control.

Anything in the text line before ``$<`` is discarded.

A special place holder, ``$<``, is used to slurp whatever text that comes
between it and the next line. It's useful for eating up extraneous newlines.
Example::

text line 0
$<text line 1
$<text line 2

will generate the following output::

For instance::
text line 0
text line 1
text line 2

This allows you some flexibility in indenting the source template lines, making
them more readable.

Anything in the text line after ``$>`` is discarded.

Example::

this is the end of the file
#end template
will generate a newline after "file" since it's considered part of the
textline, whereas::

this is the end of the file$<
this is the end of the file$>
#end template

will ensure that no newline occurrs at the end of the output.
Expand All @@ -353,7 +383,7 @@ Template Execution
When a template function executes, it has a set of variables defined on entry.
Here they are in definition order:

* :ref:`python import <import_directive>` aliases
* :ref:`python import <import_directive>` aliases, including the :ref:`_utils <utility_classes>` autoimport
* :ref:`template load <load_directive>` aliases
* module :ref:`attributes <attribute_directive>`
* other :ref:`template function <template_directive>` names within the same module
Expand Down Expand Up @@ -405,3 +435,16 @@ available in any of the three modules.

See the :ref:`html sample <test_samples>` for example usage.

.. _utility_classes:

Utility Classes
================

Texthon provides some utility classes/functions to aid template formatting.
They are automatically imported under the ``_utils`` module alias.

See the `nest <http://github.com/kevinic/texthon/tree/master/tests/nest>`_
sample for templates that rely on the ``Indent`` class to dynamically
control whitespace.

:ref:`More details <utility_module>`.
20 changes: 9 additions & 11 deletions sphinx/source/index.rst
Expand Up @@ -70,29 +70,27 @@ If only it wouldn't bug me about that stupid Namemapper extension all the time.

Example
===================================
Here's a snippet showing sample template text, along with the code
that evaluates it::
Here's a simple template file, ``hello.tmpl.txt`` ::

import texthon

template = """
#template main(who, count)
$who says:
#{for i in range(0, count):
hello world!
#}
#end template
"""

engine = texthon.Engine()
engine.load_text(template, "@root")
engine.make()
using the script ``texthon`` with the following parameters::

print(engine.modules["@root"].main("someone", 2))
texthon hello.tmpl.txt -P who=someone -P count=2

which prints the following output::
the following output is generated::

someone says:
hello world!
hello world!


For an example that's closer to a real-world scenario, `these templates <http://github.com/kevinic/texthon/tree/master/tests/cpp>`_
demonstrate the use of Texthon to write C++ object reflection code: a task that
would normally require tedious copy-and-pasting and/or C++ template/macro black
magic.
Empty file added tests/cpp/__init__.py
Empty file.
55 changes: 55 additions & 0 deletions tests/cpp/main.cpp
@@ -0,0 +1,55 @@
#include "rtt.h"
#include <stdio.h>

using namespace game;

void print_r(unsigned int level, char const* name, rtt::typeinfo_base const* type, void* ptr)
{
for(unsigned int i = 0; i < level; ++i)
{
printf(" ");
}

switch(type->get_id())
{
case rtt::type_traits<float>::id:
printf("%s: %f\n", name, *type->cast<float>(ptr));
break;

case rtt::type_traits<int>::id:
printf("%s: %i\n", name, *type->cast<int>(ptr));
break;

default:
printf("%s:\n", name);
for(unsigned int field = 0; field < type->field_count(); ++field)
{
print_r(level + 1, type->field_name(field), type->field_type(field), type->get_field(ptr, field));
}
break;
}
}

template <class T> void print(T& obj)
{
print_r(0, "obj", rtt::get_type(&obj), &obj);
}

int main(int, char**)
{
rtt::create();

transform trans = {
{0, 0, 1, 0},
{0, 1, 0, 0},
{1, 0, 0, 0},
{0, 0, 0, 1},
};
object obj;
obj.health = 100;
obj.xform = trans;
print(obj);

rtt::destroy();
return 0;
}
11 changes: 11 additions & 0 deletions tests/cpp/processor.py
@@ -0,0 +1,11 @@
def norm(types):
typeId = 0
for type in types:
type["id"] = typeId

if "extern" not in type:
type["extern"] = False
if "members" not in type:
type["members"] = []

typeId += 1
31 changes: 31 additions & 0 deletions tests/cpp/reflection.tmpl.cpp
@@ -0,0 +1,31 @@

namespace texthon {

struct type_lib
{
int get_typeid(int* t)
{
return 0;
}
};

struct type_info
{
unsigned int field_count;

void* get_field(void* ptr, unsigned int field);
type_info const* get_field_type(void* ptr, unsigned int field);

virtual void on_visit(void* p)
{
T* actual = static_cast<T>(p);

}
};

template <class T> T* lib::try_cast(type_info const& type, void* ptr)
{
id = type.lib.map<T>;
}

}

0 comments on commit 1112a5c

Please sign in to comment.