Experimental hack to unify mustache and mustache_template libraries
diff --git a/lib/mustache.dart b/lib/mustache.dart
index 687459c..f6c6ccc 100644
--- a/lib/mustache.dart
+++ b/lib/mustache.dart
@@ -12,7 +12,8 @@
       bool htmlEscapeValues,
       String name,
       PartialResolver partialResolver,
-      String delimiters}) = t.Template.fromSource;
+      String delimiters,
+      ValueResolver valueResolver}) = t.Template.fromSource;
 
   String get name;
   String get source;
@@ -30,6 +31,27 @@
 
 typedef PartialResolver = Template Function(String);
 
+const Object noSuchProperty = Object();
+
+typedef ValueResolver = Object Function(Object, Object);
+
+final RegExp _integerTag = RegExp(r'^[0-9]+$');
+
+//FIXME should name be String??
+// Returns the property of the given object by name. For a map,
+// 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.
+Object defaultValueResolver(Object object, Object name) {
+  if (object is Map && object.containsKey(name)) return object[name];
+
+  if (object is List && _integerTag.hasMatch(name)) {
+    return object[int.parse(name)];
+  }
+
+  return noSuchProperty;
+}
+
 typedef LambdaFunction = Object Function(LambdaContext context);
 
 /// Passed as an argument to a mustache lambda function. The methods on
diff --git a/lib/mustache_with_mirrors.dart b/lib/mustache_with_mirrors.dart
new file mode 100644
index 0000000..29bc8be
--- /dev/null
+++ b/lib/mustache_with_mirrors.dart
@@ -0,0 +1,66 @@
+import 'dart:mirrors';
+
+export 'mustache.dart' hide Template;
+import 'mustache.dart' as m;
+import 'src/template.dart' as t;
+
+class Template extends t.Template {
+  Template(String source,
+      {bool lenient = false,
+      bool htmlEscapeValues = true,
+      String name,
+      m.PartialResolver partialResolver,
+      String delimiters = '{{ }}',
+      m.ValueResolver valueResolver})
+      : super.fromSource(source,
+            lenient: lenient,
+            htmlEscapeValues: htmlEscapeValues,
+            name: name,
+            partialResolver: partialResolver,
+            delimiters: delimiters,
+            valueResolver:
+            valueResolver ??
+                (lenient ? lenientMirrorValueResolver : mirrorValueResolver));
+}
+
+final RegExp _validTag = RegExp(r'^[0-9a-zA-Z\_\-\.]+$');
+final RegExp _integerTag = RegExp(r'^[0-9]+$');
+
+Object mirrorValueResolver(Object object, Object name) =>
+    _mirrorValueResolver(object, name, lenient: false);
+
+Object lenientMirrorValueResolver(Object object, Object name) =>
+    _mirrorValueResolver(object, name, lenient: true);
+
+//FIXME name should be string right?
+// Returns the property of the given object by name. For a map,
+// 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.
+Object _mirrorValueResolver(Object object, Object name,
+    {bool lenient = false}) {
+  if (object is Map && object.containsKey(name)) return object[name];
+
+  if (object is List && _integerTag.hasMatch(name)) {
+    return object[int.parse(name)];
+  }
+
+  if (lenient && !_validTag.hasMatch(name)) return m.noSuchProperty;
+
+  var instance = reflect(object);
+  var field = instance.type.instanceMembers[Symbol(name)];
+  if (field == null) return m.noSuchProperty;
+
+  var invocation;
+  if ((field is VariableMirror) ||
+      ((field is MethodMirror) && (field.isGetter))) {
+    invocation = instance.getField(field.simpleName);
+  } else if ((field is MethodMirror) &&
+      (field.parameters.where((p) => !p.isOptional).isEmpty)) {
+    invocation = instance.invoke(field.simpleName, []);
+  }
+  if (invocation == null) {
+    return m.noSuchProperty;
+  }
+  return invocation.reflectee;
+}
diff --git a/lib/src/renderer.dart b/lib/src/renderer.dart
index 74ca3cf..56e95f0 100644
--- a/lib/src/renderer.dart
+++ b/lib/src/renderer.dart
@@ -4,12 +4,18 @@
 import 'template.dart';
 import 'template_exception.dart';
 
-const Object noSuchProperty = Object();
-final RegExp _integerTag = RegExp(r'^[0-9]+$');
 
 class Renderer extends Visitor {
-  Renderer(this.sink, List stack, this.lenient, this.htmlEscapeValues,
-      this.partialResolver, this.templateName, this.indent, this.source)
+  Renderer(
+      this.sink,
+      List stack,
+      this.lenient,
+      this.htmlEscapeValues,
+      this.partialResolver,
+      this.valueResolver,
+      this.templateName,
+      this.indent,
+      this.source)
       : _stack = List.from(stack);
 
   Renderer.partial(Renderer ctx, Template partial, String indent)
@@ -19,24 +25,42 @@
             ctx.lenient,
             ctx.htmlEscapeValues,
             ctx.partialResolver,
+            ctx.valueResolver,
             ctx.templateName,
             ctx.indent + indent,
             partial.source);
 
   Renderer.subtree(Renderer ctx, StringSink sink)
-      : this(sink, ctx._stack, ctx.lenient, ctx.htmlEscapeValues,
-            ctx.partialResolver, ctx.templateName, ctx.indent, ctx.source);
+      : this(
+            sink,
+            ctx._stack,
+            ctx.lenient,
+            ctx.htmlEscapeValues,
+            ctx.partialResolver,
+            ctx.valueResolver,
+            ctx.templateName,
+            ctx.indent,
+            ctx.source);
 
   Renderer.lambda(Renderer ctx, String source, String indent, StringSink sink,
       String delimiters)
-      : this(sink, ctx._stack, ctx.lenient, ctx.htmlEscapeValues,
-            ctx.partialResolver, ctx.templateName, ctx.indent + indent, source);
+      : this(
+            sink,
+            ctx._stack,
+            ctx.lenient,
+            ctx.htmlEscapeValues,
+            ctx.partialResolver,
+            ctx.valueResolver,
+            ctx.templateName,
+            ctx.indent + indent,
+            source);
 
   final StringSink sink;
   final List _stack;
   final bool lenient;
   final bool htmlEscapeValues;
   final m.PartialResolver partialResolver;
+  final m.ValueResolver valueResolver;
   final String templateName;
   final String indent;
   final String source;
@@ -92,7 +116,7 @@
       context.close();
     }
 
-    if (value == noSuchProperty) {
+    if (value == m.noSuchProperty) {
       if (!lenient) {
         throw error('Value was missing for variable tag: ${node.name}.', node);
       }
@@ -130,7 +154,7 @@
     } else if (value == false) {
       // Do nothing.
 
-    } else if (value == noSuchProperty) {
+    } else if (value == m.noSuchProperty) {
       if (!lenient) {
         throw error('Value was missing for section tag: ${node.name}.', node);
       }
@@ -155,7 +179,7 @@
     } else if (value == true || value is Map || value is Iterable) {
       // Do nothing.
 
-    } else if (value == noSuchProperty) {
+    } else if (value == m.noSuchProperty) {
       if (lenient) {
         _renderWithValue(node, null);
       } else {
@@ -208,36 +232,22 @@
       return _stack.last;
     }
     var parts = name.split('.');
-    var object = noSuchProperty;
+    var object = m.noSuchProperty;
     for (var o in _stack.reversed) {
-      object = _getNamedProperty(o, parts[0]);
-      if (object != noSuchProperty) {
+      object = valueResolver(o, parts[0]);
+      if (object != m.noSuchProperty) {
         break;
       }
     }
     for (var i = 1; i < parts.length; i++) {
-      if (object == null || object == noSuchProperty) {
-        return noSuchProperty;
+      if (object == null || object == m.noSuchProperty) {
+        return m.noSuchProperty;
       }
-      object = _getNamedProperty(object, parts[i]);
+      object = valueResolver(object, parts[i]);
     }
     return object;
   }
 
-  // Returns the property of the given object by name. For a map,
-  // 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.
-  Object _getNamedProperty(dynamic object, dynamic name) {
-    if (object is Map && object.containsKey(name)) return object[name];
-
-    if (object is List && _integerTag.hasMatch(name)) {
-      return object[int.parse(name)];
-    }
-
-    return noSuchProperty;
-  }
-
   m.TemplateException error(String message, Node node) =>
       TemplateException(message, templateName, source, node.start);
 
diff --git a/lib/src/template.dart b/lib/src/template.dart
index a38cc97..9f7349b 100644
--- a/lib/src/template.dart
+++ b/lib/src/template.dart
@@ -9,13 +9,15 @@
       bool htmlEscapeValues = true,

       String name,

       m.PartialResolver partialResolver,

-      String delimiters = '{{ }}'})

+      String delimiters = '{{ }}',

+      m.ValueResolver valueResolver = m.defaultValueResolver})

       : source = source,

         _nodes = parser.parse(source, lenient, name, delimiters),

         _lenient = lenient,

         _htmlEscapeValues = htmlEscapeValues,

         _name = name,

-        _partialResolver = partialResolver;

+        _partialResolver = partialResolver,

+        _valueResolver = valueResolver;

 

   @override

   final String source;

@@ -24,6 +26,7 @@
   final bool _htmlEscapeValues;

   final String _name;

   final m.PartialResolver _partialResolver;

+  final m.ValueResolver _valueResolver;

 

   @override

   String get name => _name;

@@ -38,7 +41,7 @@
   @override

   void render(values, StringSink sink) {

     var renderer = Renderer(sink, [values], _lenient, _htmlEscapeValues,

-        _partialResolver, _name, '', source);

+        _partialResolver, _valueResolver, _name, '', source);

     renderer.render(_nodes);

   }

 }