Add more error messages, and change imports
diff --git a/lib/mustache.dart b/lib/mustache.dart
index 26d83b8..4a6ca6f 100644
--- a/lib/mustache.dart
+++ b/lib/mustache.dart
@@ -1,8 +1,8 @@
+/// [Mustache template documentation](http://mustache.github.com/mustache.5.html)
+
 library mustache;
 
-import 'src/mustache_impl.dart' as impl;
-
-/// [Mustache template documentation](http://mustache.github.com/mustache.5.html)
+import 'src/template.dart' as t;
 
 /// Use new Template(source) instead.
 @deprecated
@@ -21,7 +21,7 @@
       {bool lenient,
        bool htmlEscapeValues,
        String name,
-       PartialResolver partialResolver}) = impl.Template.fromSource;
+       PartialResolver partialResolver}) = t.Template.fromSource;
   
   String get name;
   String get source;
diff --git a/lib/src/lambda_context.dart b/lib/src/lambda_context.dart
index 465dc65..b2e24e2 100644
--- a/lib/src/lambda_context.dart
+++ b/lib/src/lambda_context.dart
@@ -1,4 +1,11 @@
-part of mustache.impl;
+library mustache.lambda_context;
+
+import 'package:mustache/mustache.dart' as m;
+
+import 'node.dart';
+import 'parser.dart' as parser;
+import 'render_context.dart';
+import 'template_exception.dart';
 
 /// Passed as an argument to a mustache lambda function.
 class LambdaContext implements m.LambdaContext {
@@ -46,7 +53,7 @@
     _checkClosed();
     if (_node is! SectionNode) _error(
         'LambdaContext.render() can only be called on section tags.');
-    _renderSubtree(_context._sink, value);
+    _renderSubtree(_context.sink, value);
   }
 
   void write(Object object) {
@@ -83,7 +90,7 @@
       delimiters = node.delimiters;
     }
     
-    var nodes = parse(source,
+    var nodes = parser.parse(source,
         _context.lenient,
         _context.templateName,
         delimiters);
diff --git a/lib/src/mustache_impl.dart b/lib/src/mustache_impl.dart
deleted file mode 100644
index 0c11e5a..0000000
--- a/lib/src/mustache_impl.dart
+++ /dev/null
@@ -1,16 +0,0 @@
-library mustache.impl;
-
-@MirrorsUsed(metaTargets: const [m.mustache])
-import 'dart:mirrors';
-
-import 'package:mustache/mustache.dart' as m;
-
-import 'parser.dart' as parser;
-
-part 'lambda_context.dart';
-part 'node.dart';
-part 'parse.dart';
-part 'render_context.dart';
-part 'scanner.dart';
-part 'template.dart';
-part 'token.dart';
diff --git a/lib/src/node.dart b/lib/src/node.dart
index 670f453..d5653da 100644
--- a/lib/src/node.dart
+++ b/lib/src/node.dart
@@ -1,4 +1,8 @@
-part of mustache.impl;
+library mustache.node;
+
+import 'lambda_context.dart';
+import 'render_context.dart';
+import 'template.dart';
 
 void renderWithContext(RenderContext ctx, List<Node> nodes) {
   if (ctx.indent == null || ctx.indent == '') {
@@ -104,7 +108,7 @@
       context.close();
     }
     
-    if (value == _noSuchProperty) {
+    if (value == noSuchProperty) {
       if (!ctx.lenient) 
         throw ctx.error('Value was missing for variable tag: ${name}.', this);
     } else {
@@ -201,7 +205,7 @@
     } else if (value == false) {
       // Do nothing.
     
-    } else if (value == _noSuchProperty) {
+    } else if (value == noSuchProperty) {
       if (!renderer.lenient)
         throw renderer.error('Value was missing for section tag: ${name}.', this);
     
@@ -230,7 +234,7 @@
     } else if (value == true || value is Map || value is Iterable) {
       // Do nothing.
     
-    } else if (value == _noSuchProperty) {
+    } else if (value == noSuchProperty) {
       if (ctx.lenient) {
         _renderWithValue(ctx, null);
       } else {
@@ -284,7 +288,7 @@
         : ctx.partialResolver(partialName);
     if (template != null) {
       var partialCtx = new RenderContext.partial(ctx, template, this.indent);
-      renderWithContext(partialCtx, template._nodes);
+      renderWithContext(partialCtx, template.getNodes());
     } else if (ctx.lenient) {
       // do nothing
     } else {
@@ -292,3 +296,11 @@
     }
   }
 }
+
+const int _AMP = 38;
+const int _LT = 60;
+const int _GT = 62;
+const int _QUOTE = 34;
+const int _APOS = 39;
+const int _FORWARD_SLASH = 47;
+const int _NEWLINE = 10;
diff --git a/lib/src/parse.dart b/lib/src/parse.dart
deleted file mode 100644
index 5748a66..0000000
--- a/lib/src/parse.dart
+++ /dev/null
@@ -1,189 +0,0 @@
-part of mustache.impl;
-
-List<Node> parse(String source,
-             bool lenient,
-             String templateName,
-             String delimiters) {
-  
-  if (source == null) throw new ArgumentError.notNull('Template source');
-  
-  var tokens = 
-      new Scanner(source, templateName, delimiters, lenient: lenient).scan();
-  
-  tokens = _removeStandaloneWhitespace(tokens);
-  tokens = _mergeAdjacentText(tokens);
-  
-  var stack = new List<Node>()..add(new SectionNode('root', 0, 0, delimiters));
-
-  var delim;
-  
-  for (var t in tokens) {
-    switch (t.type) {
-      case _TEXT:
-        var n = new TextNode(t.value, t.start, t.end);
-        stack.last.children.add(n);
-        break;
-        
-      case _VARIABLE:
-      case _UNESC_VARIABLE:
-        var n = new VariableNode(
-            t.value, t.start, t.end, escape: t.type != _UNESC_VARIABLE);
-        stack.last.children.add(n);
-        break;
-
-      case _PARTIAL:
-        var n = new PartialNode(t.value, t.start, t.end, t.indent); 
-        stack.last.children.add(n);
-        break;
-
-      case _OPEN_SECTION:
-      case _OPEN_INV_SECTION:
-        // Store the start, end of the inner string content not
-        // including the tag.
-        var child = new SectionNode(t.value, t.start, t.end, delim, 
-            inverse: t.type == _OPEN_INV_SECTION)
-            ..contentStart = t.end;
-        stack.last.children.add(child);
-        stack.add(child);
-        break;
-
-      case _CLOSE_SECTION:
-        if (stack.last.name != t.value) {
-          throw new TemplateException(
-            "Mismatched tag, expected: '${stack.last.name}', was: '${t.value}'",
-            templateName, source, t.start);
-        }
-  
-        stack.last.contentEnd = t.start;
-        
-        stack.removeLast();
-        break;
-      
-      case _CHANGE_DELIMITER:
-        delim = t.value;
-        break;
-        
-      case _COMMENT:
-        // Do nothing
-        break;
-      
-      //FIXME change constants to enums, and then remove this default clause.
-      default:
-        throw new StateError('Unkown node type: $t');
-    }
-  }
-
-  if (stack.length != 1) {
-    throw new TemplateException(
-      "Unclosed tag: '${stack.last.name}'.",
-      templateName, source, stack.last.start);
-  }
-  
-  return stack.last.children;
-}
-
-// Takes a list of tokens, and removes _NEWLINE, and _WHITESPACE tokens.
-// This is used to implement mustache standalone lines.
-// Where TAG is one of: OPEN_SECTION, INV_SECTION, CLOSE_SECTION
-// LINE_END, [WHITESPACE], TAG, [WHITESPACE], LINE_END => LINE_END, TAG
-// WHITESPACE => TEXT
-// LINE_END => TEXT
-// TODO could rewrite this to use a generator, rather than creating an inter-
-// mediate list.
-List<Token> _removeStandaloneWhitespace(List<Token> tokens) {
-  int i = 0;
-  Token read() { var ret = i < tokens.length ? tokens[i++] : null; return ret; }
-  Token peek([int n = 0]) => i + n < tokens.length ? tokens[i + n] : null;
-
-  bool isTag(token) => token != null
-      && const [_OPEN_SECTION, _OPEN_INV_SECTION, _CLOSE_SECTION, _COMMENT,
-                _PARTIAL, _CHANGE_DELIMITER].contains(token.type);
-
-  bool isWhitespace(token) => token != null && token.type == _WHITESPACE;
-  bool isLineEnd(token) => token != null && token.type == _LINE_END;
-
-  var result = new List<Token>();
-  add(token) => result.add(token);
-
-  standaloneLineCheck() {
-    // Swallow leading whitespace 
-    // Note, the scanner will only ever create a single whitespace token. There
-    // is no need to handle multiple whitespace tokens.
-    if (isWhitespace(peek())
-        && isTag(peek(1))
-        && (isLineEnd(peek(2)) || peek(2) == null)) { // null == EOF
-      read();
-    } else if (isWhitespace(peek())
-        && isTag(peek(1))
-        && isWhitespace(peek(2))
-        && (isLineEnd(peek(3)) || peek(3) == null)) {
-      read();
-    }
-
-    if ((isTag(peek()) && isLineEnd(peek(1)))
-        || (isTag(peek()) 
-            && isWhitespace(peek(1))
-            && (isLineEnd(peek(2)) || peek(2) == null))) {      
-
-      // Add tag
-      add(read());
-
-      // Swallow trailing whitespace.
-      if (isWhitespace(peek()))
-        read();
-
-      // Swallow line end.
-      assert(isLineEnd(peek()));
-      read();
-
-      standaloneLineCheck(); //FIXME don't use recursion.
-    }
-  }
-
-  // Handle case where first line is a standalone tag.
-  standaloneLineCheck();
-
-  var t;
-  while ((t = read()) != null) {
-    if (t.type == _LINE_END) {
-      // Convert line end to text token
-      add(new Token(_TEXT, t.value, t.start, t.end));
-      standaloneLineCheck();
-    } else if (t.type == _WHITESPACE) {
-      // Convert whitespace to text token
-      add(new Token(_TEXT, t.value, t.start, t.end));
-    } else {
-      // Preserve token
-      add(t);
-    }
-  }
-
-  return result;
-}
-
-// Merging adjacent text nodes will improve the render speed, but slow down
-// parsing. It will be beneficial where templates are parsed once and rendered
-// a number of times.
-List<Token> _mergeAdjacentText(List<Token> tokens) {
-  if (tokens.isEmpty) return <Token>[];
-  
-  var result = new List<Token>();
-  int i = 0;
-  while(i < tokens.length) {
-    var t = tokens[i];
-    
-    if (t.type != _TEXT
-        || (i < tokens.length - 1 && tokens[i + 1].type != _TEXT)) {
-      result.add(tokens[i]);
-      i++;
-    } else {
-      var buffer = new StringBuffer();
-      while(i < tokens.length && tokens[i].type == _TEXT) {
-        buffer.write(tokens[i].value);
-        i++;
-      }
-      result.add(new Token(_TEXT, buffer.toString(), t.start, t.end));
-    }
-  }
-  return result;
-}
diff --git a/lib/src/parser.dart b/lib/src/parser.dart
index bdcb65c..c202eff 100644
--- a/lib/src/parser.dart
+++ b/lib/src/parser.dart
@@ -1,10 +1,9 @@
-library parser;
+library mustache.parser;
 
-//TODO just import nodes.
-import 'mustache_impl.dart' show Node, SectionNode, TextNode, PartialNode, VariableNode;
-import 'scanner2.dart';
+import 'node.dart';
+import 'scanner.dart';
 import 'template_exception.dart';
-import 'token2.dart';
+import 'token.dart';
 
 List<Node> parse(String source,
              bool lenient,
@@ -20,8 +19,6 @@
   final String name;
   final int start;
   final int end;
-  //TODO parse the tag contents.
-  //final List<List<String>> arguments;
 }
 
 class TagType {
@@ -49,7 +46,7 @@
 
 TagType tagTypeFromString(String s) { 
   var type = _tagTypeMap[s];
-  if (type == null) throw 'boom!'; //TODO unreachable.
+  if (type == null) throw new Exception('Unreachable code.');
   return type;
 }
 
@@ -60,7 +57,6 @@
     // _scanner = new Scanner(_source, _templateName, _delimiters, _lenient);
   }
   
-  //TODO do I need to keep all of these variables around?
   final String _source;
   final bool _lenient;
   final String _templateName;
@@ -124,7 +120,11 @@
         
       // {{/...}}
       case TagType.closeSection:
-        if (tag.name != _stack.last.name) throw 'boom!'; //TODO error message.
+        if (tag.name != _stack.last.name) {
+          throw new TemplateException("Mismatched tag, expected: "
+              "'${_stack.last.name}', was: '${tag.name}'",
+              _templateName, _source, tag.start);
+        }
         var node = _stack.removeLast();
         node.contentEnd = tag.start;
         break;        
@@ -141,11 +141,9 @@
       case TagType.changeDelimiter:
         // Ignore.
         break;
-        
-      //TODO soon will be able to omit this as the analyzer will warn if a case
-      // is missing for an enum.
+
       default:
-        throw 'boom!'; //TODO unreachable.
+        throw new Exception('Unreachable code.');
     }
   }
   
@@ -183,18 +181,18 @@
           break;
           
         case TokenType.lineEnd:
-          //TODO the first line can be a standalone line too, and there is
-          // no lineEnd. Perhaps _parseLine(firstLine: true)?
           _parseLine();
           break;
           
         default:
-          throw 'boom!'; //TODO error message.
+          throw new Exception('Unreachable code.');
       }
     }
     
-    //TODO proper error message.
-    assert(_stack.length == 1);
+    if (_stack.length != 1) {
+      throw new TemplateException("Unclosed tag: '${_stack.last.name}'.",
+          _templateName, _source, _stack.last.start);
+    }
     
     return _stack.last.children;
   }
@@ -319,51 +317,77 @@
         node = null;
         break;
 
-      //TODO remove this default.
       default:
-        throw 'boom!'; //TODO error message. Unreachable.
+        throw new Exception('Unreachable code');
     }
     return node;
   }
-    
+  
+  final RegExp _validIdentifier = new RegExp(r'^[0-9a-zA-Z\_\-\.]+$');
+  
+  //TODO clean up EOF handling.
   Tag _readTag() {
     
     var open = _read();
-     
+    
+    checkEof() {
+      if (_peek() == null) {
+        throw _error(
+            'Tag not closed before the end of the template.', open.start);
+      }
+    }
+ 
+    checkEof();
+    
+    //TODO perhaps handle Eof in readif
     //TODO _readIf()
     if (_peek().type == TokenType.whitespace) _read();
     
+    checkEof();
+    
     // A sigil is the character which identifies which sort of tag it is,
     // i.e.  '#', '/', or '>'.
     // Variable tags and triple mustache tags don't have a sigil.
     TagType tagType = _peek().type == TokenType.sigil
       ? tagTypeFromString(_read().value)
       : (open.value == '{{{' ? TagType.tripleMustache : TagType.variable);
-    
-    //TODO if tagType is comment, then ignore content. i.e. make sure parsing
-    // doesn't crash.
+
+    checkEof();
       
     if (_peek().type == TokenType.whitespace) _read();
     
+    checkEof();
+    
     // TODO split up names here instead of during render.
     // Also check that they are valid token types.
-    var name = _parseIdentifier();
-    
-    var close = _read();
-    
-    return new Tag(tagType, name, open.start, close.end);
-  }
-  
-  //TODO shouldn't just return a string.
-  String _parseIdentifier() {
     // TODO split up names here instead of during render.
     // Also check that they are valid token types.
     var name = _readWhile((t) => t.type != TokenType.closeDelimiter)
          .map((t) => t.value)
          .join()
          .trim();
+  
+    checkEof();
     
-    return name;
-  } 
+    // Check to see if tag name is valid.
+    if (tagType != TagType.comment) {    
+      if (name == '') throw _error('Empty tag', open.start);
+      
+      if (name.contains('\t') || name.contains('\n') || name.contains('\r')) {
+        throw _error('Tags may not contain newlines or tabs.', open.start);
+      }    
+      
+      if (!_lenient && !_validIdentifier.hasMatch(name)) {
+        throw _error('Unless in lenient mode, tags may only contain the '
+            'characters a-z, A-Z, minus, underscore and period.', open.start);
+      }    
+    }
+    
+    var close = _read();
+        
+    return new Tag(tagType, name, open.start, close.end);
+  }
+    
+  TemplateException _error(String msg, int offset) => 
+      new TemplateException(msg, _templateName, _source, offset);
 }
-
diff --git a/lib/src/render_context.dart b/lib/src/render_context.dart
index a3b6e78..8c7b3d9 100644
--- a/lib/src/render_context.dart
+++ b/lib/src/render_context.dart
@@ -1,13 +1,20 @@
-part of mustache.impl;
+library mustache.render_context;
+
+@MirrorsUsed(metaTargets: const [m.mustache])
+import 'dart:mirrors';
+import 'node.dart';
+import 'package:mustache/mustache.dart' as m;
+import 'template.dart';
+import 'template_exception.dart';
 
 final RegExp _validTag = new RegExp(r'^[0-9a-zA-Z\_\-\.]+$');
 final RegExp _integerTag = new RegExp(r'^[0-9]+$');
 
-const Object _noSuchProperty = const Object();
+const Object noSuchProperty = const Object();
 
 class RenderContext {
   
-  RenderContext(this._sink,
+  RenderContext(this.sink,
       List stack,
       this.lenient,
       this.htmlEscapeValues,
@@ -18,7 +25,7 @@
     : _stack = new List.from(stack); 
   
   RenderContext.partial(RenderContext ctx, Template partial, String indent)
-      : this(ctx._sink,
+      : this(ctx.sink,
           ctx._stack,
           ctx.lenient,
           ctx.htmlEscapeValues,
@@ -52,7 +59,7 @@
            ctx.indent + indent,
            source);
    
-  final StringSink _sink;
+  final StringSink sink;
   final List _stack;
   final bool lenient;
   final bool htmlEscapeValues;
@@ -65,7 +72,7 @@
   
   Object pop() => _stack.removeLast();
   
-  write(Object output) => _sink.write(output.toString());
+  write(Object output) => sink.write(output.toString());
     
   // Walks up the stack looking for the variable.
   // Handles dotted names of the form "a.b.c".
@@ -74,16 +81,16 @@
       return _stack.last;
     }
     var parts = name.split('.');
-    var object = _noSuchProperty;
+    var object = noSuchProperty;
     for (var o in _stack.reversed) {
       object = _getNamedProperty(o, parts[0]);
-      if (object != _noSuchProperty) {
+      if (object != noSuchProperty) {
         break;
       }
     }
     for (int i = 1; i < parts.length; i++) {
-      if (object == null || object == _noSuchProperty) {
-        return _noSuchProperty;
+      if (object == null || object == noSuchProperty) {
+        return noSuchProperty;
       }
       object = _getNamedProperty(object, parts[i]);
     }
@@ -103,11 +110,11 @@
       return object[int.parse(name)];
     
     if (lenient && !_validTag.hasMatch(name))
-      return _noSuchProperty;
+      return noSuchProperty;
     
     var instance = reflect(object);
     var field = instance.type.instanceMembers[new Symbol(name)];
-    if (field == null) return _noSuchProperty;
+    if (field == null) return noSuchProperty;
     
     var invocation = null;
     if ((field is VariableMirror) || ((field is MethodMirror) && (field.isGetter))) {
@@ -116,7 +123,7 @@
       invocation = instance.invoke(field.simpleName, []);
     }
     if (invocation == null) {
-      return _noSuchProperty;
+      return noSuchProperty;
     }
     return invocation.reflectee;
   }
diff --git a/lib/src/scanner.dart b/lib/src/scanner.dart
index e370f45..8c0b248 100644
--- a/lib/src/scanner.dart
+++ b/lib/src/scanner.dart
@@ -1,388 +1,395 @@
-part of mustache.impl;

-

-class Scanner {

-  

-	Scanner(String source, this._templateName, String delimiters, {bool lenient: true})

-	 : _source = source,

-	   _lenient = lenient,

-	   _itr = source.runes.iterator {

-	  

-	  if (source == null) throw new ArgumentError.notNull('Template source');

-	  

-	  var delims = _parseDelimiterString(delimiters);

-    _openDelimiter = delims[0];

-    _openDelimiterInner = delims[1];

-    _closeDelimiterInner = delims[2];

-    _closeDelimiter = delims[3];

-    

-    if (source == '') {

-      _c = _EOF;

-    } else {

-      _itr.moveNext();

-      _c = _itr.current;

-    }

-	}

-

-	final String _templateName;

-	final String _source;

-	final bool _lenient;

-	

-  final Iterator<int> _itr;

-  int _offset = 0;

-  int _c = 0;

-	

-	final List<Token> _tokens = new List<Token>();

-

-	// These can be changed by the change delimiter tag.

-	int _openDelimiter;

-  int _openDelimiterInner;

-  int _closeDelimiterInner;

-  int _closeDelimiter;

-

-  List<Token> scan() {

-    while(true) {

-      int c = _peek();

-      if (c == _EOF) break;

-      else if (c == _openDelimiter) _scanMustacheTag();

-      else _scanText();

-    }

-    return _tokens;

-  }

-	

-  int _peek() => _c;

-  

-  int _read() {

-    var c = _c;

-    if (_itr.moveNext()) {

-      _offset++;

-      _c = _itr.current;

-    } else {

-      _c = _EOF;

-    }

-    return c;

-  }

-  

-  String _readWhile(bool test(int charCode), [Function endOfFile]) {

-    

-    int start = _offset;  

-    while (_peek() != _EOF && test(_peek())) {

-      _read();

-    }

-

-    if (_peek() == _EOF && endOfFile != null) endOfFile();

-    

-    int end = _peek() == _EOF ? _source.length : _offset;

-    return _source.substring(start, end);

-  }

-  

-	_expect(int expectedCharCode) {

-		int c = _read();

-

-		if (c == _EOF) {

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

-			    _templateName, _source, _offset);

-

-		} else if (c != expectedCharCode) {

-			throw new TemplateException('Unexpected character, '

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

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

-				_templateName, _source, _offset);

-		}

-	}

-	

-	bool _isWhitespace(int c)

-	  => const [_SPACE, _TAB , _NEWLINE, _RETURN].contains(c);

-	

-	// A sigil is the word commonly used to describe the special character at the

-	// start of mustache tag i.e. #, ^ or /.

-	bool _isSigil(int c)

-	 => const [_HASH, _CARET, _FORWARD_SLASH, _GT, _AMP, _EXCLAIM, _EQUAL]

-	   .contains(c);

-	

-	bool _isAlphanum(int c) 

-    => (c >= _a && c <= _z)

-        || (c >= _A && c <= _Z)

-        || (c >= _0 && c <= _9)

-        || c == _MINUS

-        || c == _UNDERSCORE

-        || c == _PERIOD;

-

-	_scanText() {

-	  

-		while(true) {

-		  int c = _peek();

-		  int start = _offset;

-		  

-		  if (c == _EOF) {

-		    return; 

-		  

-		  } else if (c == _openDelimiter) { 

-			  return;

-			  

-      // Newlines and whitespace have separate tokens so the standalone lines

-			// logic can be implemented.

-		  } else if (c == _NEWLINE) {

-        _read();

-        var value = new String.fromCharCode(c);

-        _tokens.add(new Token(_LINE_END, value, start, _offset));

-			  

-		  } else if (c == _RETURN) {

-        _read();

-        if (_peek() == _NEWLINE) {

-          _read();

-          _tokens.add(new Token(_LINE_END, '\r\n', start, _offset));

-        } else {

-          _tokens.add(new Token(_TEXT, '\r', start, _offset));

-        }			  

-			

-			} else if (c == _SPACE || c == _TAB) {

-        var value = _readWhile((c) => c == _SPACE || c == _TAB);

-        _tokens.add(new Token(_WHITESPACE, value, start, _offset));

-        

-			} else {

-        var value = _readWhile((c) => c != _openDelimiter && c != _NEWLINE);

-        _tokens.add(new Token(_TEXT, value, start, _offset));

-			}

-		}	

-	}

-		

-  void _scanMustacheTag() {

-    int start = _offset;

-    int sigil = 0;

-     

-    _expect(_openDelimiter);

-    

-    // If just a single delimeter character then this is a text token.

-    if (_openDelimiterInner != null && _peek() != _openDelimiterInner) {

-      var value = new String.fromCharCode(_openDelimiter);

-      _tokens.add(new Token(_TEXT, value, start, _offset));

-      return;

-    }

-    

-    if (_openDelimiterInner != null) _expect(_openDelimiterInner);

-     

-    if (_peek() == _OPEN_MUSTACHE) {

-      _scanTripleMustacheTag(start);

-      return;

-    }

- 

-    _scanTagWhitespace();

- 

-    if (_isSigil(_peek())) sigil = _read();

- 

-    if (sigil == _EQUAL) {

-      _scanChangeDelimiterTag(start);

-      return;

-    } else if (sigil == _EXCLAIM) {

-      _scanCommentTag(start);

-      return;

-    }

- 

-    _scanTagWhitespace();

- 

-    var identifier = _scanTagIdentifier();

-    

-    if (identifier.isEmpty) throw _error('Expected tag identifier.');

-    

-    _scanTagWhitespace();

- 

-    if (_closeDelimiterInner != null) _expect(_closeDelimiterInner);

-    _expect(_closeDelimiter);

-

-    const sigils = const <int, int> {

-      0: _VARIABLE,

-      _HASH: _OPEN_SECTION,

-      _FORWARD_SLASH: _CLOSE_SECTION,

-      _CARET: _OPEN_INV_SECTION,

-      _GT: _PARTIAL,

-      _AMP: _UNESC_VARIABLE

-    };

-    

-    var type = sigils[sigil];

-    var indent = type == _PARTIAL ? _getPrecedingWhitespace() : ''; 

-    

-    _tokens.add(new Token(type, identifier, start, _offset, indent: indent));

-  }

-	

-  _errorEofInTag() => throw _error('Tag not closed before the end of the template.');

-  

-  _scanTagWhitespace() {

-    if (_lenient) {

-      _readWhile(_isWhitespace, _errorEofInTag);

-    } else {

-      _readWhile((c) => c == _SPACE, _errorEofInTag);

-      if (_isWhitespace(_peek()))

-        throw _error('Tags may not contain newlines or tabs.');

-    }

-  }

-  

-  String _scanTagIdentifier({bool tripleMo: false}) {

-    var delim = _closeDelimiterInner != null

-        ? _closeDelimiterInner

-        : _closeDelimiter;

-    if (_lenient) {

-      return _readWhile(

-          (c) => c != delim 

-                 || tripleMo && c != _CLOSE_MUSTACHE,

-          _errorEofInTag).trim();

-    } else {

-      var id = _readWhile(_isAlphanum, _errorEofInTag);

-      _scanTagWhitespace();

-      if (_peek() != delim) throw _error('Unless in lenient mode tags may only '

-          'contain the characters a-z, A-Z, minus, underscore and period.');

-      return id;

-    }

-  }

-  

-  // Capture whitespace preceding a partial tag so it can used for indentation

-  // during rendering.

-  String _getPrecedingWhitespace() {

-    var indent = '';

-    if (_tokens.isNotEmpty) {

-      if (_tokens.length == 1 && _tokens.last.type == _WHITESPACE) {

-        indent = _tokens.last.value;

-      

-      } else if (_tokens.length > 1) {

-        if (_tokens.last.type == _WHITESPACE

-            && _tokens[_tokens.length - 2].type == _NEWLINE) {

-          indent = _tokens.last.value;

-        }

-      }

-    }

-    return indent;

-  }

-  

-  void _scanTripleMustacheTag(int start) {

-    _expect(_OPEN_MUSTACHE);

-    var value = _scanTagIdentifier();

-    _expect(_CLOSE_MUSTACHE);

-    if (_closeDelimiterInner != null) _expect(_closeDelimiterInner);

-    _expect(_closeDelimiter);

-    _tokens.add(new Token(_UNESC_VARIABLE, value, start, _offset));

-  }

-  

-  void _scanCommentTag(int start) {

-    var value = _closeDelimiterInner != null

-        ? _readWhile((c) => c != _closeDelimiterInner, _errorEofInTag).trim()

-        : _readWhile((c) => c != _closeDelimiter, _errorEofInTag).trim();

-    if (_closeDelimiterInner != null) _expect(_closeDelimiterInner);

-    _expect(_closeDelimiter);

-    _tokens.add(new Token(_COMMENT, value, start, _offset));

-  }

-

-  //TODO consider changing the parsing here to use a regexp. It will probably

-  // be simpler to read.

-  void _scanChangeDelimiterTag(int start) {

-    // Open delimiter characters and = have already been read.

-    

-    var delimiterInner = _closeDelimiterInner;

-    var delimiter = _closeDelimiter;

-    

-    _scanTagWhitespace();

-    

-    int c;

-    c = _read();

-    

-    if (c == _EQUAL) throw _error('Incorrect change delimiter tag.');

-    _openDelimiter = c;

-    

-    c = _read();

-    if (_isWhitespace(c)) {

-      _openDelimiterInner = null;

-    } else {

-      _openDelimiterInner = c;

-    }

-    

-    _scanTagWhitespace();

-    

-    c = _read();

-    

-    if (_isWhitespace(c) || c == _EQUAL)

-      throw _error('Incorrect change delimiter tag.');

-    

-    if (_isWhitespace(_peek()) || _peek() == _EQUAL) {

-      _closeDelimiterInner = null;

-      _closeDelimiter = c;

-    } else {

-      _closeDelimiterInner = c;

-      _closeDelimiter = _read();

-    }

-    

-    _scanTagWhitespace();

-    _expect(_EQUAL);

-    _scanTagWhitespace();

-     

-     _expect(delimiterInner);

-     _expect(delimiter);

-    

-     var value = _delimiterString(

-         _openDelimiter,

-         _openDelimiterInner,

-         _closeDelimiterInner,

-         _closeDelimiter);

-          

-     _tokens.add(new Token(_CHANGE_DELIMITER, value, start, _offset));

-  }

-  

-	m.TemplateException _error(String message) {

-	  return new TemplateException(message, _templateName, _source, _offset);

-	}

-

-}

-

-_delimiterString(int open, int openInner, int closeInner, int close) {

-  var buffer = new StringBuffer();

-  buffer.writeCharCode(open);

-  if (openInner != null) buffer.writeCharCode(openInner);

-  buffer.write(' ');

-  if (closeInner != null) buffer.writeCharCode(closeInner);

-  buffer.writeCharCode(close);

-  return buffer.toString();

-}

-

-List<int> _parseDelimiterString(String s) {

-  if (s == null) return [_OPEN_MUSTACHE, _OPEN_MUSTACHE,

-                         _CLOSE_MUSTACHE, _CLOSE_MUSTACHE];

-  if (s.length == 3) {

-    return [s.codeUnits[0], null, null, s.codeUnits[2]];

-  

-  } else if (s.length == 5) {

-    return [s.codeUnits[0],

-            s.codeUnits[1],

-            s.codeUnits[3],

-            s.codeUnits[4]];

-  } else {

-    throw new TemplateException(

-        'Invalid delimiter string $s', null, null, null);

-  }  

-}

-

-const int _EOF = -1;

-const int _TAB = 9;

-const int _NEWLINE = 10;

-const int _RETURN = 13;

-const int _SPACE = 32;

-const int _EXCLAIM = 33;

-const int _QUOTE = 34;

-const int _APOS = 39;

-const int _HASH = 35;

-const int _AMP = 38;

-const int _PERIOD = 46;

-const int _FORWARD_SLASH = 47;

-const int _LT = 60;

-const int _EQUAL = 61;

-const int _GT = 62;

-const int _CARET = 94;

-

-const int _OPEN_MUSTACHE = 123;

-const int _CLOSE_MUSTACHE = 125;

-

-const int _A = 65;

-const int _Z = 90;

-const int _a = 97;

-const int _z = 122;

-const int _0 = 48;

-const int _9 = 57;

-

-const int _UNDERSCORE = 95;

-const int _MINUS = 45;

+library mustache.scanner;
+
+import 'token.dart';
+import 'template_exception.dart';
+
+class Scanner {
+  
+  Scanner(String source, this._templateName, String delimiters,
+      {bool lenient: true})
+   : _source = source,
+     _lenient = lenient,
+     _itr = source.runes.iterator {
+    
+    var delims = _parseDelimiterString(delimiters);
+    _openDelimiter = delims[0];
+    _openDelimiterInner = delims[1];
+    _closeDelimiterInner = delims[2];
+    _closeDelimiter = delims[3];
+    
+    if (source == '') {
+      _c = _EOF;
+    } else {
+      _itr.moveNext();
+      _c = _itr.current;
+    }
+  }
+
+  final String _templateName;
+  final String _source;
+  final bool _lenient;
+  
+  final Iterator<int> _itr;
+  int _offset = 0;
+  int _c = 0;
+  
+  final List<Token> _tokens = new List<Token>();
+
+  // These can be changed by the change delimiter tag.
+  int _openDelimiter;
+  int _openDelimiterInner;
+  int _closeDelimiterInner;
+  int _closeDelimiter;
+  
+  List<Token> scan() {
+    
+    for (int c = _peek(); c != _EOF; c = _peek()) {
+      
+      // Scan text tokens.
+      if (c != _openDelimiter) {
+        _scanText();
+        continue;
+      }
+        
+      int start = _offset;
+      
+      // Read first open delimiter character.
+      _read();
+      
+      // If only a single delimiter character then create a text token.
+      if (_openDelimiterInner != null && _peek() != _openDelimiterInner) {
+        var value = new String.fromCharCode(_openDelimiter);
+        _append(TokenType.text, value, start, _offset);
+        continue;
+      }
+        
+      if (_openDelimiterInner != null) _expect(_openDelimiterInner);
+      
+      // Handle triple mustache.
+      if (_openDelimiterInner == _OPEN_MUSTACHE &&
+          _openDelimiter == _OPEN_MUSTACHE &&
+          _peek() == _OPEN_MUSTACHE) {
+        
+        _read();
+        _append(TokenType.openDelimiter, '{{{', start, _offset);
+        _scanTagContent();
+        _scanCloseTripleMustache();
+                   
+      } else {
+      
+        // Check to see if this is a change delimiter tag. {{= | | =}}
+        // Need to skip whitespace and check for "=".
+        int wsStart = _offset;
+        var ws = _readWhile(_isWhitespace);
+        
+        if (_peek() == _EQUAL) {
+          _parseChangeDelimiterTag(start);
+          
+        } else {
+          // Scan standard mustache tag.
+          var value = new String.fromCharCodes(_openDelimiterInner == null
+              ? [_openDelimiter]
+              : [_openDelimiter, _openDelimiterInner]);
+          
+          _append(TokenType.openDelimiter, value, start, wsStart);
+          
+          if (ws != '') _append(TokenType.whitespace, ws, wsStart, _offset);
+          
+          _scanTagContent();
+          _scanCloseDelimiter();
+        }
+      }
+    }
+    return _tokens;
+  }
+  
+  int _peek() => _c;
+  
+  int _read() {
+    int c = _c;
+    _offset++;
+    _c = _itr.moveNext() ? _itr.current : _EOF;
+    return c;
+  }
+  
+  String _readWhile(bool test(int charCode)) {
+    int start = _offset;  
+    while (_peek() != _EOF && test(_peek())) {
+      _read();
+    }
+    int end = _peek() == _EOF ? _source.length : _offset;
+    return _source.substring(start, end);
+  }
+  
+  //TODO clean up error handling.
+  _expect(int expectedCharCode) {
+    int c = _read();
+
+    if (c == _EOF) {
+      throw new TemplateException('Unexpected end of input',
+          _templateName, _source, _offset);
+
+    } else if (c != expectedCharCode) {
+      throw new TemplateException('Unexpected character, '
+        'expected: ${new String.fromCharCode(expectedCharCode)}, '
+        'was: ${new String.fromCharCode(c)}', _templateName, _source, _offset);
+    }
+  }
+  
+  _append(TokenType type, String value, int start, int end) =>
+      _tokens.add(new Token(type, value, start, end));
+  
+  bool _isWhitespace(int c)
+    => const [_SPACE, _TAB , _NEWLINE, _RETURN].contains(c);
+  
+  // Scan text. This adds text tokens, line end tokens, and whitespace
+  // tokens for whitespace at the begining of a line. This is because the
+  // mustache spec requires special handing of whitespace.
+  void _scanText() {
+    int start = 0;
+    TokenType token;
+    String value;
+  
+    for (int c = _peek(); c != _EOF && c != _openDelimiter; c = _peek()) {
+      start = _offset;
+      
+      switch (c) {
+        case _SPACE:
+        case _TAB:
+          value = _readWhile((c) => c == _SPACE || c == _TAB);
+          token = TokenType.whitespace;          
+          break;
+          
+        case _NEWLINE:
+          _read();
+          token = TokenType.lineEnd;
+          value = '\n';
+          break;
+        
+        case _RETURN:
+          _read();
+          if (_peek() == _NEWLINE) {
+            _read();
+            token = TokenType.lineEnd;
+            value = '\r\n';
+          } else {
+            token = TokenType.text;
+            value = '\r';
+          }
+          break;
+          
+        default:
+          value = _readWhile((c) => c != _openDelimiter && c != _NEWLINE);
+          token = TokenType.text;
+      }
+      
+      _append(token, value, start, _offset);
+    }
+  }
+  
+  // Scan contents of a tag and the end delimiter token.
+  void _scanTagContent() {
+    
+    int start;
+    TokenType token;
+    String value;
+    List<Token> result = <Token>[];
+    
+    bool isCloseDelimiter(int c) => 
+          (_closeDelimiterInner == null && c == _closeDelimiter)
+          || (_closeDelimiterInner != null && c == _closeDelimiterInner);      
+    
+    for (int c = _peek(); c != _EOF && !isCloseDelimiter(c); c = _peek()) {
+      
+      start = _offset;
+      
+      switch (c) {
+        case _HASH:
+        case _CARET:
+        case _FORWARD_SLASH:
+        case _GT:
+        case _AMP:
+        case _EXCLAIM:        
+          _read();
+          token = TokenType.sigil;
+          value = new String.fromCharCode(c);
+          break;
+          
+        case _SPACE:
+        case _TAB:
+        case _NEWLINE:
+        case _RETURN:
+          token = TokenType.whitespace;
+          value = _readWhile(_isWhitespace);
+          break;
+          
+        case _PERIOD:
+          _read();
+          token = TokenType.dot;
+          value = '.';  
+          break;
+          
+        default:          
+          // Indentifier can be any other character in lenient mode.
+          token = TokenType.identifier;
+          value = _readWhile((c) => !(const [ _HASH, _CARET, _FORWARD_SLASH,
+            _GT, _AMP, _EXCLAIM, _EQUAL, _SPACE, _TAB, _NEWLINE, _RETURN,
+            _PERIOD].contains(c)) &&
+            c != _closeDelimiterInner &&
+            c != _closeDelimiter);
+      }
+      _append(token, value, start, _offset);    
+    }    
+  }
+  
+  // Scan close delimiter token.
+  void _scanCloseDelimiter() {
+    
+    if (_peek() != _EOF) {
+      int start = _offset;
+      
+      if (_closeDelimiterInner != null) _expect(_closeDelimiterInner);
+      _expect(_closeDelimiter);
+      
+      String value = new String.fromCharCodes(_closeDelimiterInner == null
+          ? [_closeDelimiter]
+          : [_closeDelimiterInner, _closeDelimiter]);
+      
+      _append(TokenType.closeDelimiter, value, start, _offset);
+    }    
+  }
+
+  // Scan close triple mustache delimiter token.
+  void _scanCloseTripleMustache() {
+    if (_peek() != _EOF) {
+      int start = _offset;
+      
+      _expect(_CLOSE_MUSTACHE);
+      _expect(_CLOSE_MUSTACHE);      
+      _expect(_CLOSE_MUSTACHE);
+      
+      _append(TokenType.closeDelimiter, '}}}', start, _offset);
+    }    
+  }  
+
+  //TODO EOF handling
+  // Open delimiter characters have already been read.
+  void _parseChangeDelimiterTag(int start) {
+    
+    _expect(_EQUAL);
+    
+    var delimiterInner = _closeDelimiterInner;
+    var delimiter = _closeDelimiter;
+    
+    _readWhile(_isWhitespace);
+    
+    int c;
+    c = _read();
+    
+    if (c == _EQUAL) throw _error('Incorrect change delimiter tag.');
+    _openDelimiter = c;
+    
+    c = _read();
+    if (_isWhitespace(c)) {
+      _openDelimiterInner = null;
+    } else {
+      _openDelimiterInner = c;
+    }
+    
+    _readWhile(_isWhitespace);
+    
+    c = _read();
+    
+    if (_isWhitespace(c) || c == _EQUAL)
+      throw _error('Incorrect change delimiter tag.');
+    
+    if (_isWhitespace(_peek()) || _peek() == _EQUAL) {
+      _closeDelimiterInner = null;
+      _closeDelimiter = c;
+    } else {
+      _closeDelimiterInner = c;
+      _closeDelimiter = _read();
+    }
+    
+    _readWhile(_isWhitespace);
+    
+    _expect(_EQUAL);
+    
+    _readWhile(_isWhitespace);     
+     
+    if (delimiterInner != null) _expect(delimiterInner);
+     _expect(delimiter);
+    
+     var value = _delimiterString(
+         _openDelimiter,
+         _openDelimiterInner,
+         _closeDelimiterInner,
+         _closeDelimiter);
+          
+     _append(TokenType.changeDelimiter, value, start, _offset);
+  }
+  
+  TemplateException _error(String message) {
+    return new TemplateException(message, _templateName, _source, _offset);
+  }
+
+}
+
+_delimiterString(int open, int openInner, int closeInner, int close) {
+  var buffer = new StringBuffer();
+  buffer.writeCharCode(open);
+  if (openInner != null) buffer.writeCharCode(openInner);
+  buffer.write(' ');
+  if (closeInner != null) buffer.writeCharCode(closeInner);
+  buffer.writeCharCode(close);
+  return buffer.toString();
+}
+
+List<int> _parseDelimiterString(String s) {
+  if (s == null) return [_OPEN_MUSTACHE, _OPEN_MUSTACHE,
+                         _CLOSE_MUSTACHE, _CLOSE_MUSTACHE];
+  if (s.length == 3) {
+    return [s.codeUnits[0], null, null, s.codeUnits[2]];
+  
+  } else if (s.length == 5) {
+    return [s.codeUnits[0],
+            s.codeUnits[1],
+            s.codeUnits[3],
+            s.codeUnits[4]];
+  } else {
+    throw new TemplateException(
+        'Invalid delimiter string $s', null, null, null);
+  }  
+}
+
+const int _EOF = -1;
+const int _TAB = 9;
+const int _NEWLINE = 10;
+const int _RETURN = 13;
+const int _SPACE = 32;
+const int _EXCLAIM = 33;
+const int _QUOTE = 34;
+const int _APOS = 39;
+const int _HASH = 35;
+const int _AMP = 38;
+const int _PERIOD = 46;
+const int _FORWARD_SLASH = 47;
+const int _LT = 60;
+const int _EQUAL = 61;
+const int _GT = 62;
+const int _CARET = 94;
+
+const int _OPEN_MUSTACHE = 123;
+const int _CLOSE_MUSTACHE = 125;
+
+const int _A = 65;
+const int _Z = 90;
+const int _a = 97;
+const int _z = 122;
+const int _0 = 48;
+const int _9 = 57;
+
+const int _UNDERSCORE = 95;
+const int _MINUS = 45;
+
+
diff --git a/lib/src/scanner2.dart b/lib/src/scanner2.dart
deleted file mode 100644
index d5af146..0000000
--- a/lib/src/scanner2.dart
+++ /dev/null
@@ -1,394 +0,0 @@
-library mustache.scanner;
-
-import 'token2.dart';
-import 'template_exception.dart';
-
-class Scanner {
-  
-  Scanner(String source, this._templateName, String delimiters,
-      {bool lenient: true})
-   : _source = source,
-     _lenient = lenient,
-     _itr = source.runes.iterator {
-    
-    var delims = _parseDelimiterString(delimiters);
-    _openDelimiter = delims[0];
-    _openDelimiterInner = delims[1];
-    _closeDelimiterInner = delims[2];
-    _closeDelimiter = delims[3];
-    
-    if (source == '') {
-      _c = _EOF;
-    } else {
-      _itr.moveNext();
-      _c = _itr.current;
-    }
-  }
-
-  final String _templateName;
-  final String _source;
-  final bool _lenient;
-  
-  final Iterator<int> _itr;
-  int _offset = 0;
-  int _c = 0;
-  
-  final List<Token> _tokens = new List<Token>();
-
-  // These can be changed by the change delimiter tag.
-  int _openDelimiter;
-  int _openDelimiterInner;
-  int _closeDelimiterInner;
-  int _closeDelimiter;
-  
-  List<Token> scan() {
-    
-    for (int c = _peek(); c != _EOF; c = _peek()) {
-      
-      // Scan text tokens.
-      if (c != _openDelimiter) {
-        _scanText();
-        continue;
-      }
-        
-      int start = _offset;
-      
-      // Read first open delimiter character.
-      _read();
-      
-      // If only a single delimiter character then create a text token.
-      if (_openDelimiterInner != null && _peek() != _openDelimiterInner) {
-        var value = new String.fromCharCode(_openDelimiter);
-        _push(TokenType.text, value, start, _offset);
-        continue;
-      }
-        
-      if (_openDelimiterInner != null) _expect(_openDelimiterInner);
-      
-      // Handle triple mustache.
-      if (_openDelimiterInner == _OPEN_MUSTACHE &&
-          _openDelimiter == _OPEN_MUSTACHE &&
-          _peek() == _OPEN_MUSTACHE) {
-        
-        _read();
-        _push(TokenType.openDelimiter, '{{{', start, _offset);
-        _scanTagContent();
-        _scanCloseTripleMustache();
-                   
-      } else {
-      
-        // Check to see if this is a change delimiter tag. {{= | | =}}
-        // Need to skip whitespace and check for "=".
-        int wsStart = _offset;
-        var ws = _readWhile(_isWhitespace);
-        
-        if (_peek() == _EQUAL) {
-          _parseChangeDelimiterTag(start);
-          
-        } else {
-          // Scan standard mustache tag.
-          var value = new String.fromCharCodes(_openDelimiterInner == null
-              ? [_openDelimiter]
-              : [_openDelimiter, _openDelimiterInner]);
-          
-          _push(TokenType.openDelimiter, value, start, wsStart);
-          
-          if (ws != '') _push(TokenType.whitespace, ws, wsStart, _offset);
-          
-          _scanTagContent();
-          _scanCloseDelimiter();
-        }
-      }
-    }
-    return _tokens;
-  }
-  
-  int _peek() => _c;
-  
-  int _read() {
-    int c = _c;
-    _offset++;
-    _c = _itr.moveNext() ? _itr.current : _EOF;
-    return c;
-  }
-  
-  String _readWhile(bool test(int charCode)) {
-    int start = _offset;  
-    while (_peek() != _EOF && test(_peek())) {
-      _read();
-    }
-    int end = _peek() == _EOF ? _source.length : _offset;
-    return _source.substring(start, end);
-  }
-  
-  _expect(int expectedCharCode) {
-    int c = _read();
-
-    if (c == _EOF) {
-      throw new TemplateException('Unexpected end of input',
-          _templateName, _source, _offset);
-
-    } else if (c != expectedCharCode) {
-      throw new TemplateException('Unexpected character, '
-        'expected: ${new String.fromCharCode(expectedCharCode)}, '
-        'was: ${new String.fromCharCode(c)}', _templateName, _source, _offset);
-    }
-  }
-  
-  // TODO rename this.
-  _push(TokenType type, String value, int start, int end) =>
-      _tokens.add(new Token(type, value, start, end));
-  
-  bool _isWhitespace(int c)
-    => const [_SPACE, _TAB , _NEWLINE, _RETURN].contains(c);
-  
-  // Scan text. This adds text tokens, line end tokens, and whitespace
-  // tokens for whitespace at the begining of a line. This is because the
-  // mustache spec requires special handing of whitespace.
-  void _scanText() {
-    int start = 0;
-    TokenType token;
-    String value;
-  
-    for (int c = _peek(); c != _EOF && c != _openDelimiter; c = _peek()) {
-      start = _offset;
-      
-      switch (c) {
-        case _SPACE:
-        case _TAB:
-          value = _readWhile((c) => c == _SPACE || c == _TAB);
-          token = TokenType.whitespace;          
-          break;
-          
-        case _NEWLINE:
-          _read();
-          token = TokenType.lineEnd;
-          value = '\n';
-          break;
-        
-        case _RETURN:
-          _read();
-          if (_peek() == _NEWLINE) {
-            _read();
-            token = TokenType.lineEnd;
-            value = '\r\n';
-          } else {
-            token = TokenType.text;
-            value = '\r';
-          }
-          break;
-          
-        default:
-          value = _readWhile((c) => c != _openDelimiter && c != _NEWLINE);
-          token = TokenType.text;
-      }
-      
-      _push(token, value, start, _offset);
-    }
-  }
-  
-  // Scan contents of a tag and the end delimiter token.
-  void _scanTagContent() {
-    
-    int start;
-    TokenType token;
-    String value;
-    List<Token> result = <Token>[];
-    
-    bool isCloseDelimiter(int c) => 
-          (_closeDelimiterInner == null && c == _closeDelimiter)
-          || (_closeDelimiterInner != null && c == _closeDelimiterInner);      
-    
-    for (int c = _peek(); c != _EOF && !isCloseDelimiter(c); c = _peek()) {
-      
-      start = _offset;
-      
-      switch (c) {
-        case _HASH:
-        case _CARET:
-        case _FORWARD_SLASH:
-        case _GT:
-        case _AMP:
-        case _EXCLAIM:        
-          _read();
-          token = TokenType.sigil;
-          value = new String.fromCharCode(c);
-          break;
-          
-        case _SPACE:
-        case _TAB:
-        case _NEWLINE:
-        case _RETURN:
-          token = TokenType.whitespace;
-          value = _readWhile(_isWhitespace);
-          break;
-          
-        case _PERIOD:
-          _read();
-          token = TokenType.dot;
-          value = '.';  
-          break;
-          
-        default:          
-          // Indentifier can be any other character in lenient mode.
-          token = TokenType.identifier;
-          value = _readWhile((c) => !(const [ _HASH, _CARET, _FORWARD_SLASH,
-            _GT, _AMP, _EXCLAIM, _EQUAL, _SPACE, _TAB, _NEWLINE, _RETURN,
-            _PERIOD].contains(c)) &&
-            c != _closeDelimiterInner &&
-            c != _closeDelimiter);
-      }
-      _push(token, value, start, _offset);    
-    }    
-  }
-  
-  // Scan close delimiter token.
-  void _scanCloseDelimiter() {
-    
-    if (_peek() != _EOF) {
-      int start = _offset;
-      
-      if (_closeDelimiterInner != null) _expect(_closeDelimiterInner);
-      _expect(_closeDelimiter);
-      
-      String value = new String.fromCharCodes(_closeDelimiterInner == null
-          ? [_closeDelimiter]
-          : [_closeDelimiterInner, _closeDelimiter]);
-      
-      _push(TokenType.closeDelimiter, value, start, _offset);
-    }    
-  }
-
-  // Scan close triple mustache delimiter token.
-  void _scanCloseTripleMustache() {
-    if (_peek() != _EOF) {
-      int start = _offset;
-      
-      _expect(_CLOSE_MUSTACHE);
-      _expect(_CLOSE_MUSTACHE);      
-      _expect(_CLOSE_MUSTACHE);
-      
-      _push(TokenType.closeDelimiter, '}}}', start, _offset);
-    }    
-  }  
-
-  // Open delimiter characters and = have already been read.
-  void _parseChangeDelimiterTag(int start) {
-    
-    _expect(_EQUAL);
-    
-    var delimiterInner = _closeDelimiterInner;
-    var delimiter = _closeDelimiter;
-    
-    _readWhile(_isWhitespace);
-    
-    int c;
-    c = _read();
-    
-    if (c == _EQUAL) throw _error('Incorrect change delimiter tag.');
-    _openDelimiter = c;
-    
-    c = _read();
-    if (_isWhitespace(c)) {
-      _openDelimiterInner = null;
-    } else {
-      _openDelimiterInner = c;
-    }
-    
-    _readWhile(_isWhitespace);
-    
-    c = _read();
-    
-    if (_isWhitespace(c) || c == _EQUAL)
-      throw _error('Incorrect change delimiter tag.');
-    
-    if (_isWhitespace(_peek()) || _peek() == _EQUAL) {
-      _closeDelimiterInner = null;
-      _closeDelimiter = c;
-    } else {
-      _closeDelimiterInner = c;
-      _closeDelimiter = _read();
-    }
-    
-    _readWhile(_isWhitespace);
-    
-    _expect(_EQUAL);
-    
-    _readWhile(_isWhitespace);     
-     
-    if (delimiterInner != null) _expect(delimiterInner);
-     _expect(delimiter);
-    
-     var value = _delimiterString(
-         _openDelimiter,
-         _openDelimiterInner,
-         _closeDelimiterInner,
-         _closeDelimiter);
-          
-     _push(TokenType.changeDelimiter, value, start, _offset);
-  }
-  
-  TemplateException _error(String message) {
-    return new TemplateException(message, _templateName, _source, _offset);
-  }
-
-}
-
-_delimiterString(int open, int openInner, int closeInner, int close) {
-  var buffer = new StringBuffer();
-  buffer.writeCharCode(open);
-  if (openInner != null) buffer.writeCharCode(openInner);
-  buffer.write(' ');
-  if (closeInner != null) buffer.writeCharCode(closeInner);
-  buffer.writeCharCode(close);
-  return buffer.toString();
-}
-
-List<int> _parseDelimiterString(String s) {
-  if (s == null) return [_OPEN_MUSTACHE, _OPEN_MUSTACHE,
-                         _CLOSE_MUSTACHE, _CLOSE_MUSTACHE];
-  if (s.length == 3) {
-    return [s.codeUnits[0], null, null, s.codeUnits[2]];
-  
-  } else if (s.length == 5) {
-    return [s.codeUnits[0],
-            s.codeUnits[1],
-            s.codeUnits[3],
-            s.codeUnits[4]];
-  } else {
-    throw new TemplateException(
-        'Invalid delimiter string $s', null, null, null);
-  }  
-}
-
-const int _EOF = -1;
-const int _TAB = 9;
-const int _NEWLINE = 10;
-const int _RETURN = 13;
-const int _SPACE = 32;
-const int _EXCLAIM = 33;
-const int _QUOTE = 34;
-const int _APOS = 39;
-const int _HASH = 35;
-const int _AMP = 38;
-const int _PERIOD = 46;
-const int _FORWARD_SLASH = 47;
-const int _LT = 60;
-const int _EQUAL = 61;
-const int _GT = 62;
-const int _CARET = 94;
-
-const int _OPEN_MUSTACHE = 123;
-const int _CLOSE_MUSTACHE = 125;
-
-const int _A = 65;
-const int _Z = 90;
-const int _a = 97;
-const int _z = 122;
-const int _0 = 48;
-const int _9 = 57;
-
-const int _UNDERSCORE = 95;
-const int _MINUS = 45;
-
-
diff --git a/lib/src/template.dart b/lib/src/template.dart
index 8ea5ed3..ea5ad61 100644
--- a/lib/src/template.dart
+++ b/lib/src/template.dart
@@ -1,4 +1,10 @@
-part of mustache.impl;

+library mustache.template;

+

+import 'package:mustache/mustache.dart' as m;

+

+import 'node.dart';

+import 'parser.dart' as parser;

+import 'render_context.dart';

 

 class Template implements m.Template {

  

@@ -21,6 +27,9 @@
   final String _name;

   final m.PartialResolver _partialResolver;

   

+  //TODO get rid of this. Only needed for rendering partials.

+  List<Node> getNodes() => _nodes;

+  

   String get name => _name;

   

   String renderString(values) {

@@ -35,112 +44,3 @@
     renderWithContext(ctx, _nodes);

   }

 }

-

-class TemplateException implements m.TemplateException {

-

-  TemplateException(this.message, this.templateName, this.source, this.offset);

-

-  final String message;

-  final String templateName;

-  final String source;

-  final int offset;

-  

-  bool _isUpdated = false;

-  int _line;

-  int _column;

-  String _context;

-  

-  int get line {

-    _update();

-    return _line;

-  }

-

-  int get column {

-    _update();

-    return _column;

-  }

-

-  String get context {

-    _update();

-    return _context;

-  }

-    

-  String toString() {

-    var list = [];

-    if (templateName != null) list.add(templateName);

-    if (line != null) list.add(line);

-    if (column != null) list.add(column);

-    var location = list.isEmpty ? '' : ' (${list.join(':')})';     

-    return '$message$location\n$context';

-  }

-

-  // This source code is a modified version of FormatException.toString().

-  void _update() {

-    if (_isUpdated) return;

-    _isUpdated = true;

-        

-    if (source == null

-        || offset == null

-        || (offset < 0 || offset > source.length))

-      return;

-    

-    // Find line and character column.

-    int lineNum = 1;

-    int lineStart = 0;

-    bool lastWasCR;

-    for (int i = 0; i < offset; i++) {

-      int char = source.codeUnitAt(i);

-      if (char == 0x0a) {

-        if (lineStart != i || !lastWasCR) {

-          lineNum++;

-        }

-        lineStart = i + 1;

-        lastWasCR = false;

-      } else if (char == 0x0d) {

-        lineNum++;

-        lineStart = i + 1;

-        lastWasCR = true;

-      }

-    }

-    

-    _line = lineNum;

-    _column = offset - lineStart + 1;

-

-    // Find context.

-    int lineEnd = source.length;

-    for (int i = offset; i < source.length; i++) {

-      int char = source.codeUnitAt(i);

-      if (char == 0x0a || char == 0x0d) {

-        lineEnd = i;

-        break;

-      }

-    }

-    int length = lineEnd - lineStart;

-    int start = lineStart;

-    int end = lineEnd;

-    String prefix = "";

-    String postfix = "";

-    if (length > 78) {

-      // Can't show entire line. Try to anchor at the nearest end, if

-      // one is within reach.

-      int index = offset - lineStart;

-      if (index < 75) {

-        end = start + 75;

-        postfix = "...";

-      } else if (end - offset < 75) {

-        start = end - 75;

-        prefix = "...";

-      } else {

-        // Neither end is near, just pick an area around the offset.

-        start = offset - 36;

-        end = offset + 36;

-        prefix = postfix = "...";

-      }

-    }

-    String slice = source.substring(start, end);

-    int markOffset = offset - start + prefix.length;

-    

-    _context = "$prefix$slice$postfix\n${" " * markOffset}^\n";

-  }

-

-}
\ No newline at end of file
diff --git a/lib/src/token.dart b/lib/src/token.dart
index 9e9b88f..682bc89 100644
--- a/lib/src/token.dart
+++ b/lib/src/token.dart
@@ -1,46 +1,49 @@
-part of mustache.impl;
+library mustache.token;
+
+
+class TokenType {
+  
+  const TokenType(this.name);
+  
+  final String name;
+  
+  String toString() => '(TokenType $name)';
+
+  static const TokenType text = const TokenType('text');
+  static const TokenType openDelimiter = const TokenType('openDelimiter');
+  static const TokenType closeDelimiter = const TokenType('closeDelimiter');
+
+  // A sigil is the word commonly used to describe the special character at the
+  // start of mustache tag i.e. #, ^ or /.
+  static const TokenType sigil = const TokenType('sigil');
+  static const TokenType identifier = const TokenType('identifier');
+  static const TokenType dot = const TokenType('dot');
+  
+  static const TokenType changeDelimiter = const TokenType('changeDelimiter');
+  static const TokenType whitespace = const TokenType('whitespace');
+  static const TokenType lineEnd = const TokenType('lineEnd');
+
+}
+
 
 class Token {
   
-  Token(this.type, this.value, this.start, this.end, {this.indent : ''});
+  Token(this.type, this.value, this.start, this.end);
   
-  final int type;
+  final TokenType type;
   final String value;
   
   final int start;
   final int end;
   
-  // Used to store the preceding whitespace before a partial tag, so that
-  // it's content can be correctly indented.
-  final String indent;
+  String toString() => "(Token ${type.name} \"$value\" $start $end)";
   
-  toString() => "${_tokenTypeString(type)}: "
-    "\"${value.replaceAll('\n', '\\n')}\"";
+  // Only used for testing.
+  bool operator ==(o) => o is Token
+      && type == o.type
+      && value == o.value
+      && start == o.start
+      && end == o.end;
+  
+  // TODO hashcode. import quiver.
 }
-
-//FIXME use enums
-const int _TEXT = 1;
-const int _VARIABLE = 2;
-const int _PARTIAL = 3;
-const int _OPEN_SECTION = 4;
-const int _OPEN_INV_SECTION = 5;
-const int _CLOSE_SECTION = 6;
-const int _COMMENT = 7;
-const int _UNESC_VARIABLE = 8;
-const int _WHITESPACE = 9; // Should be filtered out, before returned by scan.
-const int _LINE_END = 10; // Should be filtered out, before returned by scan.
-const int _CHANGE_DELIMITER = 11;
-
-_tokenTypeString(int type) => [
-  '?', 
-  'Text',
-  'Var',
-  'Par',
-  'Open',
-  'OpenInv',
-  'Close',
-  'Comment',
-  'UnescVar',
-  'Whitespace',
-  'LineEnd',
-  'ChangeDelimiter'][type];
diff --git a/lib/src/token2.dart b/lib/src/token2.dart
deleted file mode 100644
index 682bc89..0000000
--- a/lib/src/token2.dart
+++ /dev/null
@@ -1,49 +0,0 @@
-library mustache.token;
-
-
-class TokenType {
-  
-  const TokenType(this.name);
-  
-  final String name;
-  
-  String toString() => '(TokenType $name)';
-
-  static const TokenType text = const TokenType('text');
-  static const TokenType openDelimiter = const TokenType('openDelimiter');
-  static const TokenType closeDelimiter = const TokenType('closeDelimiter');
-
-  // A sigil is the word commonly used to describe the special character at the
-  // start of mustache tag i.e. #, ^ or /.
-  static const TokenType sigil = const TokenType('sigil');
-  static const TokenType identifier = const TokenType('identifier');
-  static const TokenType dot = const TokenType('dot');
-  
-  static const TokenType changeDelimiter = const TokenType('changeDelimiter');
-  static const TokenType whitespace = const TokenType('whitespace');
-  static const TokenType lineEnd = const TokenType('lineEnd');
-
-}
-
-
-class Token {
-  
-  Token(this.type, this.value, this.start, this.end);
-  
-  final TokenType type;
-  final String value;
-  
-  final int start;
-  final int end;
-  
-  String toString() => "(Token ${type.name} \"$value\" $start $end)";
-  
-  // Only used for testing.
-  bool operator ==(o) => o is Token
-      && type == o.type
-      && value == o.value
-      && start == o.start
-      && end == o.end;
-  
-  // TODO hashcode. import quiver.
-}
diff --git a/test/mustache_test.dart b/test/mustache_test.dart
index 055f4fe..d55c5be 100644
--- a/test/mustache_test.dart
+++ b/test/mustache_test.dart
@@ -7,7 +7,7 @@
 const UNEXPECTED_EOF = 'Tag not closed';
 const BAD_VALUE_SECTION = 'Invalid value type for section';
 const BAD_VALUE_INV_SECTION = 'Invalid value type for inverse section';
-const BAD_TAG_NAME = 'Unless in lenient mode tags may only contain';
+const BAD_TAG_NAME = 'Unless in lenient mode, tags may only contain';
 const VALUE_NULL = 'Value was null or missing';
 const VALUE_MISSING = 'Value was missing';
 const UNCLOSED_TAG = 'Unclosed tag';
@@ -279,12 +279,13 @@
 		test('Unexpected EOF', () {
 			var source = '{{#section}}_{{var}}_{{/section';
 			var ex = renderFail(source, {"section": {"var": "bob"}});
-			expectFail(ex, 1, source.length, UNEXPECTED_EOF);
+			expectFail(ex, 1, 22, UNEXPECTED_EOF);
 		});
 
 		test('Bad tag name, open section', () {
 			var source = r'{{#section$%$^%}}_{{var}}_{{/section}}';
 			var ex = renderFail(source, {"section": {"var": "bob"}});
+			print(ex);
 			expectFail(ex, null, null, BAD_TAG_NAME);
 		});
 
diff --git a/test/parser_test.dart b/test/parser_test.dart
index a1c5495..9096369 100644
--- a/test/parser_test.dart
+++ b/test/parser_test.dart
@@ -1,9 +1,9 @@
 import 'package:unittest/unittest.dart';
 
-import 'package:mustache/src/mustache_impl.dart' show TextNode, VariableNode, SectionNode, PartialNode;
+import 'package:mustache/src/node.dart';
 import 'package:mustache/src/parser.dart';
-import 'package:mustache/src/scanner2.dart';
-import 'package:mustache/src/token2.dart';
+import 'package:mustache/src/scanner.dart';
+import 'package:mustache/src/token.dart';
 
 main() {