Quick hacky helpers impl - lenient mode only
diff --git a/lib/mustache.dart b/lib/mustache.dart
index 742370c..e66e447 100644
--- a/lib/mustache.dart
+++ b/lib/mustache.dart
@@ -23,26 +23,27 @@
 abstract class Template {
 
   /// The constructor parses the template source and throws [TemplateException]
-  /// if the syntax of the source is invalid.
-  /// Tag names may only contain characters a-z, A-Z, 0-9, underscore, and minus,
-  /// unless lenient mode is specified.
+  /// if the syntax of the source is invalid. Tag names may only contain the
+  /// characters a-z, A-Z, 0-9, underscore, minus, and period unless lenient
+  /// mode is specified.
   factory Template(String source,
-      {bool lenient,
-       bool htmlEscapeValues,
-       String name,
-       PartialResolver partialResolver}) = _Template.fromSource;
+      {String name,
+       bool lenient,
+       bool htmlEscapeValues,       
+       PartialResolver partialResolver,
+       Map<String,LambdaFunction> helpers}) = _Template.fromSource;
   
   String get name;
   String get source;
   
 	/// [values] can be a combination of Map, List, String. Any non-String object
-	/// will be converted using toString(). Null values will cause a 
+	/// will be converted using toString(). Missing values will cause a 
 	/// [TemplateException], unless lenient module is enabled.
 	String renderString(values);
 
 	/// [values] can be a combination of Map, List, String. Any non-String object
-	/// will be converted using toString(). Null values will cause a 
-	/// FormatException, unless lenient module is enabled.
+	/// will be converted using toString(). Missing values will cause a 
+	/// [TemplateException], unless lenient module is enabled.
 	void render(values, StringSink sink);
 }
 
@@ -82,6 +83,10 @@
   
   /// Lookup the value of a variable in the current context. 
   Object lookup(String variableName);
+  
+  bool get isHelper;
+  
+  List<String> get arguments;
 }
 
 
diff --git a/lib/src/lambda_context.dart b/lib/src/lambda_context.dart
index 158101c..5c3fdf0 100644
--- a/lib/src/lambda_context.dart
+++ b/lib/src/lambda_context.dart
@@ -6,10 +6,19 @@
   final _Node _node;
   final _Renderer _renderer;
   final bool _isSection;
+  final bool _isHelper;
+  final List<String> _arguments;
   bool _closed = false;
   
-  _LambdaContext(this._node, this._renderer, {bool isSection: true})
-      : _isSection = isSection;
+  //FIXME remove isSection parameter, can just check node.type instead.
+  // Perhaps for isHelper too, once implemented.
+  _LambdaContext(this._node, this._renderer,
+       {bool isSection: true,
+        bool isHelper: false,
+        List<String> arguments: const <String>[]})
+      : _isSection = isSection,
+        _isHelper = isHelper,
+        _arguments = arguments;
   
   void close() {
     _closed = true;
@@ -21,6 +30,12 @@
         _renderer._templateName, _renderer._source, _node.start);
   }
 
+  bool get isSection => _isSection;
+  
+  bool get isHelper => _isHelper;
+  
+  List<String> get arguments => _arguments;
+  
   //TODO is allowing adding value to the stack a good idea?? Not sure.
   String renderString({Object value}) {
     _checkClosed();
@@ -65,7 +80,8 @@
     var node = _parse(source,
         _renderer._lenient,
         _renderer._templateName,
-        delimiters);
+        delimiters,
+        _renderer._helpers);
     var renderer = new _Renderer.lambda(
         _renderer,
         node,
diff --git a/lib/src/parse.dart b/lib/src/parse.dart
index dbe4bfc..f7fb50f 100644
--- a/lib/src/parse.dart
+++ b/lib/src/parse.dart
@@ -3,7 +3,8 @@
 _Node _parse(String source,
              bool lenient,
              String templateName,
-             String delimiters) {
+             String delimiters,
+             Map<String,LambdaFunction> helpers) {
   
   if (source == null) throw new ArgumentError('Template source is null');
   
@@ -34,14 +35,18 @@
         break;
 
       case _CLOSE_SECTION:
+        
         if (stack.last.value != t.value) {
-          throw new _TemplateException(
-            "Mismatched tag, expected: '${stack.last.value}', was: '${t.value}'",
-            templateName, source, t.start);
+          //FIXME quick hack for experimental helpers support.
+          var helperName = t.value.split(new RegExp('[ \n\r\t]+')).first;
+          if (helpers == null || !helpers.containsKey(helperName)) {
+            throw new _TemplateException(
+              "Mismatched tag, expected: '${stack.last.value}', was: '${t.value}'",
+              templateName, source, t.start);
+          }
         }
   
         stack.last.contentEnd = t.start;
-        
         stack.removeLast();
         break;
       
diff --git a/lib/src/renderer.dart b/lib/src/renderer.dart
index 3c4c054..93cd113 100644
--- a/lib/src/renderer.dart
+++ b/lib/src/renderer.dart
@@ -17,7 +17,8 @@
       this._templateName,
       this._indent,
       this._source,
-      this._delimiters)
+      this._delimiters,
+      this._helpers)
     : _stack = new List.from(stack); 
   
   _Renderer.partial(_Renderer renderer, _Template partial, String indent)
@@ -31,7 +32,8 @@
           renderer._templateName,
           renderer._indent + indent,
           partial.source,
-          '{{ }}');
+          '{{ }}',
+          renderer._helpers);
 
    _Renderer.subtree(_Renderer renderer, _Node node, StringSink sink)
        : this(node,
@@ -44,7 +46,8 @@
            renderer._templateName,
            renderer._indent,
            renderer._source,
-           '{{ }}');
+           '{{ }}',
+           renderer._helpers);
 
     _Renderer.lambda(
         _Renderer renderer,
@@ -63,7 +66,8 @@
            renderer._templateName,
            renderer._indent + indent,
            source,
-           delimiters);
+           delimiters,
+           renderer._helpers);
    
   final _Node _root;
   final StringSink _sink;
@@ -75,6 +79,7 @@
   final String _templateName;
   final String _indent;
   final String _source;
+  final Map<String,LambdaFunction> _helpers;
 
   String _delimiters;
   
@@ -107,7 +112,20 @@
   
   _write(Object output) => _sink.write(output.toString());
 
-  _renderNode(_Node node) {
+  void _renderNode(_Node node) {
+    
+    //FIXME just an ugly hack for now.
+    // Perhaps make a separate helper node type.
+    // Or just a flag on node.
+    //TODO handle more types of tags - i.e. unesc_var
+    if (node.type == _VARIABLE || node.type == _OPEN_SECTION) {
+      var arguments = node.value.split(new RegExp('[ \n\t\r]+'));
+      if (_helpers != null && _helpers.containsKey(arguments[0])) {
+        _renderHelper(node);
+        return;
+      }
+    }
+    
     switch (node.type) {
       case _TEXT:
         _renderText(node);
@@ -137,7 +155,7 @@
     }
   }
 
-  _renderText(_Node node, {bool lastNode: false}) {
+  void _renderText(_Node node, {bool lastNode: false}) {
     var s = node.value;
     if (s == '') return;
     if (_indent == null || _indent == '') {
@@ -153,7 +171,7 @@
 
   // Walks up the stack looking for the variable.
   // Handles dotted names of the form "a.b.c".
-  _resolveValue(String name) {
+  Object _resolveValue(String name) {
     if (name == '.') {
       return _stack.last;
     }
@@ -178,7 +196,7 @@
   // which contains the key name, this is object[name]. For other
   // objects, this is object.name or object.name(). If no property
   // by the given name exists, this method returns noSuchProperty.
-  _getNamedProperty(object, name) {
+  Object _getNamedProperty(object, name) {
     
     if (object is Map && object.containsKey(name))
       return object[name];
@@ -205,7 +223,7 @@
     return invocation.reflectee;
   }
 
-  _renderVariable(_Node node, {bool escape : true}) {
+  void _renderVariable(_Node node, {bool escape : true}) {
     var value = _resolveValue(node.value);
     
     if (value is Function) {
@@ -226,12 +244,39 @@
     }
   }
 
-  _renderSectionWithValue(node, value) {
+  void _renderSectionWithValue(node, value) {
     _stack.add(value);
     node.children.forEach(_renderNode);
     _stack.removeLast();
   }
 
+  void _renderHelper(_Node node, {escape: true}) {
+    //FIXME just an ugly hack for now.
+    //Will only work in lenient mode. Would also be nice not to have to any
+    //argument parsing during rendering.
+    
+    var arguments = node.value.split(new RegExp('[ \n\t\r]+'));
+    var helper = _helpers[arguments[0]];
+    
+    var argValues = arguments
+        .skip(1)
+        .map((arg) => _resolveValue(arg)).toList(growable: false);
+    
+    var context = new _LambdaContext(node, this,
+        isSection: false,
+        isHelper: true,
+        arguments: argValues);
+    var value = helper(context);
+    context.close();
+    
+    var valueString = (value == null) ? '' : value.toString();
+    var output = !escape && _htmlEscapeValues
+        ? _htmlEscape(valueString)
+        : valueString;
+
+    _write(output);
+  }
+    
   void _renderSubtree(node, StringSink sink, {Object value}) {
     var renderer = new _Renderer.subtree(this, node, sink);
     if (value != null) renderer._stack.add(value);
diff --git a/lib/src/template.dart b/lib/src/template.dart
index 6dbe3b1..f1972c2 100644
--- a/lib/src/template.dart
+++ b/lib/src/template.dart
@@ -3,16 +3,18 @@
 class _Template implements Template {

  

   _Template.fromSource(String source, 

-       {bool lenient: false,

-        bool htmlEscapeValues : true,

-        String name,

-        PartialResolver partialResolver})

+       {String name,

+        bool lenient: false,

+        bool htmlEscapeValues : true,        

+        PartialResolver partialResolver,

+        Map<String,LambdaFunction> helpers})

        :  source = source,

-          _root = _parse(source, lenient, name, '{{ }}'),

+          _root = _parse(source, lenient, name, '{{ }}', helpers),

           _lenient = lenient,

           _htmlEscapeValues = htmlEscapeValues,

           _name = name,

-          _partialResolver = partialResolver;

+          _partialResolver = partialResolver,

+          _helpers = helpers;

   

   final String source;

   final _Node _root;

@@ -20,6 +22,7 @@
   final bool _htmlEscapeValues;

   final String _name;

   final PartialResolver _partialResolver;

+  final Map<String,LambdaFunction> _helpers;

   

   String get name => _name;

   

@@ -32,7 +35,7 @@
   void render(values, StringSink sink) {

     var renderer = new _Renderer(_root, sink, values, [values],

         _lenient, _htmlEscapeValues, _partialResolver, _name, '', source,

-        '{{ }}');

+        '{{ }}', _helpers);

     renderer.render();

   }

 }

diff --git a/test/mustache_test.dart b/test/mustache_test.dart
index 6d6cfb5..a7142e3 100644
--- a/test/mustache_test.dart
+++ b/test/mustache_test.dart
@@ -599,6 +599,36 @@
     });
     
   });
+
+  group('Handlerbars helpers', () {
+    
+    test("Variable", () {
+      var source = '<{{helper foo}}>';
+      var helpers = {'helper': (LambdaContext ctx) {
+        return ctx.arguments[0];
+      }};
+      var values = {'foo': 'bar'};
+      var output = '<bar>';
+      
+      var t = new Template(source, lenient: true, helpers: helpers);
+      expect(t.renderString(values), equals(output));      
+    });
+
+    test("Section", () {
+      var source = '<{{#helper foo}} oi {{/helper}}>';
+      var helpers = {'helper': (LambdaContext ctx) {
+        ctx.write(ctx.arguments[0]);
+        ctx.render();
+        ctx.write(ctx.arguments[0]);
+      }};
+      var values = {'foo': '-'};
+      var output = '<- oi ->';
+      
+      var t = new Template(source, lenient: true, helpers: helpers);
+      expect(t.renderString(values), equals(output));      
+    });
+    
+  });
   
 	group('Other', () {
 		test('Standalone line', () {