Refactor
diff --git a/lib/char_reader.dart b/lib/char_reader.dart
index 1c95597..49e1812 100644
--- a/lib/char_reader.dart
+++ b/lib/char_reader.dart
@@ -53,7 +53,7 @@
     //FIXME provide template name. Or perhaps this is a programmer error
     // and this shouldn't actually happen.
     if (peek() == _EOF)
-      throw new MustacheFormatException(
+      throw new TemplateException(
           'Unexpected end of input', null, line, column);
     
     int start = _i;
diff --git a/lib/mustache.dart b/lib/mustache.dart
index 2475c68..7a5b0d2 100644
--- a/lib/mustache.dart
+++ b/lib/mustache.dart
@@ -1,55 +1,66 @@
 library mustache;
 
-import 'dart:mirrors';
-
 part 'char_reader.dart';
 part 'scanner.dart';
 part 'template.dart';
-part 'template_renderer.dart';
 
 /// [Mustache template documentation](http://mustache.github.com/mustache.5.html)
 
-/// Returns a [Template] which can be used to render the mustache template 
-/// with substituted values.
-/// Tag names may only contain characters a-z, A-Z, 0-9, underscore, and minus,
-/// unless lenient mode is specified.
-/// Throws [MustacheFormatException] if the syntax of the source is invalid.
-Template parse(String source,
- {bool lenient : false, String templateName}) => 
-    _parse(source, lenient: lenient, templateName: templateName);
+
+/// Use new Template(source) instead.
+@deprecated
+Template parse(String source, {bool lenient : false})
+  => new Template(source, lenient: lenient);
 
 
-/// A Template can be rendered multiple times with different values.
+/// A Template can be efficienctly rendered multiple times with different
+/// values.
 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.
+  factory Template(String source,
+      {bool lenient,
+       bool htmlEscapeValues,
+       String name,
+       PartialResolver partialResolver,
+       PropertyResolver propertyResolver}) = _Template.source;
   
 	/// [values] can be a combination of Map, List, String. Any non-String object
 	/// will be converted using toString(). Null values will cause a 
-	/// [MustacheFormatException], unless lenient module is enabled.
-	String renderString(values, {bool lenient : false, bool htmlEscapeValues : true});
+	/// [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.
-	void render(values, StringSink sink, {bool lenient : false, bool htmlEscapeValues : true});
+	void render(values, StringSink sink);
 }
 
 
-/// [MustacheFormatException] is used to obtain the line and column numbers
+@deprecated
+abstract class MustacheFormatException implements FormatException {  
+}
+
+
+/// [TemplateException] is used to obtain the line and column numbers
 /// of the token which caused parse or render to fail.
-class MustacheFormatException implements Exception {
+class TemplateException implements MustacheFormatException, Exception {
   
-  factory MustacheFormatException(
+  factory TemplateException(
       String message, String template, int line, int column) {
     
     var at = template == null
         ? '$line:$column'
         : '$template:$line:$column';
     
-    return new MustacheFormatException._private(
+    return new TemplateException._private(
       '$message, at: $at.', template, line, column);
   }  
   
-  MustacheFormatException._private(
+  TemplateException._private(
       this.message, this.templateName, this.line, this.column);
   
 	final String message;
@@ -64,23 +75,24 @@
 	
 	String toString() => message;
 	
+	@deprecated
+	get source => '';
+	
+	@deprecated
+	get offset => 1;
 }
 
 
 //TODO does this require some sort of context to find partials nested in subdirs?
 typedef Template PartialResolver(String templateName);
 
+//Allows pluggable property lookup. This is so code using mirrors can be
+// plugged in without requiring the mirrors dependency in the core library.
+typedef Object PropertyResolver(Object obj, String name);
 
-// Required for handing partials
+// Useful for handing partials
 abstract class TemplateRenderer {
-
-  factory TemplateRenderer(
-      PartialResolver partialResolver,
-      {bool lenient, // FIXME not sure if this lenient works right with the partial resolver, needs to be set twice?
-       bool htmlEscapeValues}) = _TemplateRenderer;
-  
   String renderString(String templateName, values);
-  
   void render(String templateName, values, StringSink sink);
 }
 
diff --git a/lib/scanner.dart b/lib/scanner.dart
index d7f6ea2..b144b3a 100644
--- a/lib/scanner.dart
+++ b/lib/scanner.dart
@@ -168,11 +168,11 @@
 		int c = _read();

 

 		if (c == _EOF) {

-			throw new MustacheFormatException('Unexpected end of input',

+			throw new TemplateException('Unexpected end of input',

 			    _templateName, _r.line, _r.column);

 

 		} else if (c != expectedCharCode) {

-			throw new MustacheFormatException('Unexpected character, '

+			throw new TemplateException('Unexpected character, '

 				'expected: ${new String.fromCharCode(expectedCharCode)} ($expectedCharCode), '

 				'was: ${new String.fromCharCode(c)} ($c)', 

 				_templateName, _r.line, _r.column);

@@ -257,7 +257,7 @@
 

 		switch(_peek()) {

 			case _EOF:

-				throw new MustacheFormatException('Unexpected end of input',

+				throw new TemplateException('Unexpected end of input',

 				    _templateName, _r.line, _r.column);

 

 			// Escaped text {{{ ... }}}

diff --git a/lib/template.dart b/lib/template.dart
index 32736d0..cce5c2e 100644
--- a/lib/template.dart
+++ b/lib/template.dart
@@ -1,19 +1,10 @@
 part of mustache;

 

-final Object _NO_SUCH_PROPERTY = new Object();

+const Object _NO_SUCH_PROPERTY = const Object();

 

 final RegExp _validTag = new RegExp(r'^[0-9a-zA-Z\_\-\.]+$');

 final RegExp _integerTag = new RegExp(r'^[0-9]+$');

 

-Template _parse(String source, 

-                {bool lenient : false,

-                 PartialResolver partialResolver,

-                 String templateName}) {

-	var tokens = _scan(source, lenient);

-	var ast = _parseTokens(tokens, lenient, templateName);

-	return new _Template(ast, lenient, templateName);

-}

-

 _Node _parseTokens(List<_Token> tokens, bool lenient, String templateName) {

 	var stack = new List<_Node>()..add(new _Node(_OPEN_SECTION, 'root', 0, 0));

 	for (var t in tokens) {

@@ -32,7 +23,7 @@
 			_checkTagChars(t, lenient, templateName);

 

 			if (stack.last.value != t.value) {

-				throw new MustacheFormatException(

+				throw new TemplateException(

 				  "Mismatched tag, expected: '${stack.last.value}', was: '${t.value}'",

 					templateName, t.line, t.column);

 			}

@@ -53,38 +44,64 @@
 

 _checkTagChars(_Token t, bool lenient, String templateName) {

 		if (!lenient && !_validTag.hasMatch(t.value)) {

-			throw new MustacheFormatException(

+			throw new TemplateException(

 			  'Tag contained invalid characters in name, '

 				'allowed: 0-9, a-z, A-Z, underscore, and minus',

 				templateName, t.line, t.column);

 		}

 }

 

+_Node _parse(String source, bool lenient, String templateName) {

+  var tokens = _scan(source, lenient);

+  var ast = _parseTokens(tokens, lenient, templateName);

+  return ast;

+}

+

 

 class _Template implements Template {

+ 

+  _Template.source(String source, 

+       {bool lenient: false,

+        bool htmlEscapeValues : true,

+        String name,

+        PartialResolver partialResolver,

+        PropertyResolver propertyResolver})

+       :  _root = _parse(source, lenient, name),

+          _lenient = lenient,

+          _htmlEscapeValues = htmlEscapeValues,

+          _name = name,

+          _partialResolver = partialResolver,

+          _propertyResolver = propertyResolver;

   

-  _Template(this._root, this._lenient, this._name);

-  

-  final String _name;

+  // TODO share impl with _Template.source;

+  _Template.root(this._root, 

+      {bool lenient: false,

+       bool htmlEscapeValues : true,

+       String name,

+       PartialResolver partialResolver,

+       PropertyResolver propertyResolver})

+      :  _lenient = lenient,

+         _htmlEscapeValues = htmlEscapeValues,

+         _name = name,

+         _partialResolver = partialResolver,

+         _propertyResolver = propertyResolver;

+    

   final _Node _root;

   final bool _lenient;

+  final bool _htmlEscapeValues;

+  final String _name;

+  final PartialResolver _partialResolver;

+  final PropertyResolver _propertyResolver;

   

-  String renderString(values,

-                      {bool lenient : false,

-                       bool htmlEscapeValues : true,

-                       PartialResolver partialResolver}) {

+  String renderString(values) {

     var buf = new StringBuffer();

-    render(values, buf, lenient: lenient, htmlEscapeValues: htmlEscapeValues,

-        partialResolver: partialResolver);

+    render(values, buf);

     return buf.toString();

   }

 

-  void render(values, StringSink sink, 

-              {bool lenient : false,

-               bool htmlEscapeValues : true,

-               PartialResolver partialResolver}) {

+  void render(values, StringSink sink) {

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

-        lenient, htmlEscapeValues, partialResolver, _name);

+        _lenient, _htmlEscapeValues, _partialResolver, _propertyResolver, _name);

     renderer.render();

   }

 }

@@ -99,6 +116,7 @@
 	    this._lenient,

 	    this._htmlEscapeValues,

 	    this._partialResolver,

+	    this._propertyResolver,

 	    this._templateName)

     : _stack = new List.from(stack); 

 	

@@ -110,6 +128,7 @@
           renderer._lenient,

           renderer._htmlEscapeValues,

           renderer._partialResolver,

+          renderer._propertyResolver,

           renderer._templateName);

 

 	 _Renderer.subtree(_Renderer renderer, _Node node, StringSink sink)

@@ -120,6 +139,7 @@
            renderer._lenient,

            renderer._htmlEscapeValues,

            renderer._partialResolver,

+           renderer._propertyResolver,

            renderer._templateName);

 	

 	final _Node _root;

@@ -129,6 +149,7 @@
 	final bool _lenient;

 	final bool _htmlEscapeValues;

 	final PartialResolver _partialResolver;

+	final PropertyResolver _propertyResolver;

 	final String _templateName;

 

 	void render() {

@@ -196,31 +217,34 @@
 	// objects, this is object.name or object.name(). If no property

 	// by the given name exists, this method returns _NO_SUCH_PROPERTY.

 	_getNamedProperty(object, name) {

+	  

+	  if (_propertyResolver != null) return _propertyResolver(object, name);

+	  

 		var property = null;

-		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)) {

+		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 _NO_SUCH_PROPERTY;

-		}

-		var instance = reflect(object);

-		var field = instance.type.instanceMembers[new Symbol(name)];

-		if (field == null) {

-			return _NO_SUCH_PROPERTY;

-		}

-		var invocation = null;

-		if ((field is VariableMirror) || ((field is MethodMirror) && (field.isGetter))) {

-			invocation = instance.getField(field.simpleName);

-		} else if ((field is MethodMirror) && (field.parameters.length == 0)) {

-			invocation = instance.invoke(field.simpleName, []);

-		}

-		if (invocation == null) {

-			return _NO_SUCH_PROPERTY;

-		}

-		return invocation.reflectee;

+		

+// Move mirrors code into another library.

+//		var instance = reflect(object);

+//		var field = instance.type.instanceMembers[new Symbol(name)];

+//		if (field == null) return _NO_SUCH_PROPERTY;

+//		

+//		var invocation = null;

+//		if ((field is VariableMirror) || ((field is MethodMirror) && (field.isGetter))) {

+//			invocation = instance.getField(field.simpleName);

+//		} else if ((field is MethodMirror) && (field.parameters.length == 0)) {

+//			invocation = instance.invoke(field.simpleName, []);

+//		}

+//		if (invocation == null) {

+//			return _NO_SUCH_PROPERTY;

+//		}

+//		return invocation.reflectee;

 	}

 

 	_renderVariable(node, {bool escape : true}) {

@@ -230,7 +254,7 @@
 		

 		if (value == _NO_SUCH_PROPERTY) {

 			if (!_lenient)

-				throw new MustacheFormatException(

+				throw new TemplateException(

 				  'Value was missing, variable: ${node.value}',

 					_templateName, node.line, node.column);

 		} else {

@@ -275,7 +299,7 @@
 		

 		} else if (value == _NO_SUCH_PROPERTY) {

 			if (!_lenient)

-				throw new MustacheFormatException(

+				throw new TemplateException(

 				  'Value was missing, section: ${node.value}',

 					_templateName, node.line, node.column);

 		

@@ -284,7 +308,7 @@
          _write(value(output));

 		

 		} else {

-			throw new MustacheFormatException(

+			throw new TemplateException(

 			  'Invalid value type for section, '

 				'section: ${node.value}, '

         'type: ${value.runtimeType}',

@@ -308,7 +332,7 @@
 			if (_lenient) {

 				_renderSectionWithValue(node, null);

 			} else {

-				throw new MustacheFormatException(

+				throw new TemplateException(

 				    'Value was missing, inverse-section: ${node.value}',

 				    _templateName, node.line, node.column);

 			}

@@ -322,7 +346,7 @@
       }

 

 		} else {

-			throw new MustacheFormatException(

+			throw new TemplateException(

 			  'Invalid value type for inverse section, '

 				'section: ${node.value}, '

 				'type: ${value.runtimeType}, ',

@@ -339,7 +363,7 @@
     } else if (_lenient) {

       // do nothing

     } else {

-      throw new MustacheFormatException(

+      throw new TemplateException(

           'Partial not found: $partialName',

           _templateName, node.line, node.column);

     }

diff --git a/lib/template_renderer.dart b/lib/template_renderer.dart
deleted file mode 100644
index ca04aa2..0000000
--- a/lib/template_renderer.dart
+++ /dev/null
@@ -1,31 +0,0 @@
-part of mustache;
-
-class _TemplateRenderer implements TemplateRenderer {
-
-  _TemplateRenderer(PartialResolver partialResolver,
-      {bool lenient: false, bool htmlEscapeValues: true})
-      : _partialResolver = partialResolver,
-        _lenient = lenient,
-        _htmlEscapeValues = htmlEscapeValues;
-  
-  final PartialResolver _partialResolver;
-  final bool _lenient;
-  final bool _htmlEscapeValues;
-  
-  String renderString(String templateName, values) {
-    _Template template = _partialResolver(templateName);
-    return template.renderString(values, 
-        lenient: _lenient,
-        htmlEscapeValues: _htmlEscapeValues,
-        partialResolver: _partialResolver);
-  }
-  
-  void render(String templateName, values, StringSink sink) {
-    _Template template = _partialResolver(templateName);
-    template.render(values, sink, 
-        lenient: _lenient,
-        htmlEscapeValues: _htmlEscapeValues,
-        partialResolver: _partialResolver);    
-  }
-  
-}
diff --git a/test/mustache_spec_test.dart b/test/mustache_spec_test.dart
index f6fc573..1b096d6 100644
--- a/test/mustache_spec_test.dart
+++ b/test/mustache_spec_test.dart
@@ -53,7 +53,7 @@
 	var exception;
 	var trace;
 	try {
-		output = mustache.parse(template, lenient: true).renderString(data, lenient: true);
+		output = new mustache.Template(template, lenient: true).renderString(data);
 	} catch (ex, stacktrace) {
 		exception = ex;
 		trace = stacktrace;
diff --git a/test/mustache_test.dart b/test/mustache_test.dart
index 531b53b..3e1649c 100644
--- a/test/mustache_test.dart
+++ b/test/mustache_test.dart
@@ -10,6 +10,9 @@
 const BAD_TAG_NAME = 'Tag contained invalid characters in name';
 const VALUE_NULL = 'Value was null or missing';
 
+Template parse(String source, {bool lenient: false})
+  => new Template(source, lenient: lenient);
+
 main() {
 	group('Basic', () {
 		test('Variable', () {
@@ -47,7 +50,7 @@
 			var ex = renderFail(
 				'{{#section}}_{{var}}_{{/section}}',
 				{"section": 42});
-			expect(ex is MustacheFormatException, isTrue);
+			expect(ex is TemplateException, isTrue);
 			expect(ex.message, startsWith(BAD_VALUE_SECTION));
 		});
 		test('True', () {
@@ -93,7 +96,7 @@
 			var ex = renderFail(
 				'{{^section}}_{{var}}_{{/section}}',
 				{"section": 42});
-			expect(ex is MustacheFormatException, isTrue);
+			expect(ex is TemplateException, isTrue);
 			expect(ex.message, startsWith(BAD_VALUE_INV_SECTION));
 		});
 		test('True', () {
@@ -191,24 +194,24 @@
 	group('Lenient', () {
 		test('Odd section name', () {
 			var output = parse(r'{{#section$%$^%}}_{{var}}_{{/section$%$^%}}', lenient: true)
-				.renderString({r'section$%$^%': {'var': 'bob'}}, lenient: true);
+				.renderString({r'section$%$^%': {'var': 'bob'}});
 			expect(output, equals('_bob_'));
 		});
 
 		test('Odd variable name', () {
 			var output = parse(r'{{#section}}_{{var$%$^%}}_{{/section}}', lenient: true)
-				.renderString({'section': {r'var$%$^%': 'bob'}}, lenient: true);
+				.renderString({'section': {r'var$%$^%': 'bob'}});
 		});
 
 		test('Null variable', () {
 			var output = parse(r'{{#section}}_{{var}}_{{/section}}', lenient: true)
-				.renderString({'section': {'var': null}}, lenient: true);
+				.renderString({'section': {'var': null}});
 			expect(output, equals('__'));
 		});
 
 		test('Null section', () {
 			var output = parse('{{#section}}_{{var}}_{{/section}}', lenient: true)
-				.renderString({"section": null}, lenient: true);
+				.renderString({"section": null});
 			expect(output, equals(''));
 		});
 
@@ -238,24 +241,15 @@
 
 	  String _partialTest(Map values, Map sources, String renderTemplate, {bool lenient: false}) {
 	    var templates = new Map<String, Template>();
-	    for (var k in sources.keys) {
-	      templates[k] = parse(sources[k], templateName: k, lenient: lenient);
-	    }
 	    var resolver = (name) => templates[name];
-	    var renderer = new TemplateRenderer(resolver, lenient: lenient);
-	    return renderer.renderString(renderTemplate, values);
+	    for (var k in sources.keys) {
+	      templates[k] = new Template(sources[k],
+	          name: k, lenient: lenient, partialResolver: resolver);
+	    }
+	    var t = resolver(renderTemplate);
+	    return t.renderString(values);
 	  }
 	  
-		test('TemplateRenderer', () {
-		  var template = parse('{{>partial}}'); 
-		  var includedTemplate = parse('{{foo}}');
-		  var resolver = (name) => 
-		      {'root': template, 'partial': includedTemplate}[name];		  
-		  var renderer = new TemplateRenderer(resolver);
-		  var output = renderer.renderString('root', {'foo': 'bar'});
-		  expect(output, 'bar');
-    });
-		
     test('basic', () {
       var output = _partialTest(
           {'foo': 'bar'},
@@ -273,7 +267,7 @@
           'root',
           lenient: false);  
       } catch (e) {
-        expect(e is MustacheFormatException, isTrue);
+        expect(e is TemplateException, isTrue);
         print(e);
         threw = true;
       }
@@ -370,13 +364,14 @@
 		});
 	});
 
-	group('Mirrors', () {
-    test('Simple field', () {
-      var output = parse('_{{bar}}_')
-        .renderString(new Foo()..bar = 'bob');
-      expect(output, equals('_bob_'));
-    });
-  });
+// FIXME
+//	group('Mirrors', () {
+//    test('Simple field', () {
+//      var output = parse('_{{bar}}_')
+//        .renderString(new Foo()..bar = 'bob');
+//      expect(output, equals('_bob_'));
+//    });
+//  });
 }
 
 renderFail(source, values) {
@@ -389,7 +384,7 @@
 }
 
 expectFail(ex, int line, int column, [String msgStartsWith]) {
-		expect(ex is MustacheFormatException, isTrue);
+		expect(ex is TemplateException, isTrue);
 		if (line != null)
 			expect(ex.line, equals(line));
 		if (column != null)