Clean up some error messages
diff --git a/lib/src/char_reader.dart b/lib/src/char_reader.dart
index e474670..4b7dfd9 100644
--- a/lib/src/char_reader.dart
+++ b/lib/src/char_reader.dart
@@ -50,19 +50,15 @@
   
   int peek() => _c;
   
-  String readWhile(bool test(int charCode)) {
-    
-    //FIXME provide template name. Or perhaps this is a programmer error
-    // and this shouldn't actually happen.
-    if (peek() == _EOF)
-      throw new _TemplateException(
-          'Unexpected end of input', null, null, 0);
+  String readWhile(bool test(int charCode), Function endOfFile) {
     
     int start = _i;
     
     while (peek() != _EOF && test(peek())) {
       read();
     }
+
+    if (peek() == _EOF && endOfFile != null) endOfFile();
     
     int end = peek() == _EOF ? _source.length : _i;
     return _source.substring(start, end);
diff --git a/lib/src/lambda_context.dart b/lib/src/lambda_context.dart
index 80c89df..37056a9 100644
--- a/lib/src/lambda_context.dart
+++ b/lib/src/lambda_context.dart
@@ -51,7 +51,8 @@
     if (nodes.length == 1 && nodes.first.type == _TEXT)
       return nodes.first.value;
     
-    var source = _renderer._source.substring(_node.start, _node.end);
+    var source = _renderer._source.substring(
+        _node.contentStart, _node.contentEnd);
     
     return source;
   }
diff --git a/lib/src/node.dart b/lib/src/node.dart
index 7b3531c..44ab33e 100644
--- a/lib/src/node.dart
+++ b/lib/src/node.dart
@@ -4,18 +4,29 @@
   
   _Node(this.type, this.value, this.start, this.end, {this.indent});
   
-  _Node.fromToken(_Token token, {int start})
+  _Node.fromToken(_Token token)
     : type = token.type,
       value = token.value,
-      start = start == null ? token.start : start,
+      start = token.start,
       end = token.end,
       indent = token.indent;
   
   final int type;
   final String value;
+  
+  // The offset of the start of the token in the file. Unless this is a section
+  // or inverse section, then this stores the start of the content of the
+  // section.
   final int start;
-  int end;
+  final int end;
+  
+  int contentStart;
+  int contentEnd;
+  
+  // Used to store the preceding whitespace before a partial tag, so that
+  // it's content can be correctly indented.
   final String indent;
+  
   final List<_Node> children = new List<_Node>();
   
   String toString() => '_Node: ${_tokenTypeString(type)}';
diff --git a/lib/src/parse.dart b/lib/src/parse.dart
index e523667..2fc0df5 100644
--- a/lib/src/parse.dart
+++ b/lib/src/parse.dart
@@ -45,7 +45,7 @@
         checkTagChars(t);
         // Store the start, end of the inner string content not
         // including the tag.
-        var child = new _Node.fromToken(t, start: t.end);
+        var child = new _Node.fromToken(t)..contentStart = t.end;
         stack.last.children.add(child);
         stack.add(child);
         break;
@@ -59,7 +59,7 @@
             templateName, source, t.start);
         }
   
-        stack.last.end = t.start;
+        stack.last.contentEnd = t.start;
         
         stack.removeLast();
         break;
@@ -91,7 +91,7 @@
 // mediate list.
 List<_Token> _removeStandaloneWhitespace(List<_Token> tokens) {
   int i = 0;
-  _Token read() { var ret = i < tokens.length ? tokens[i++] : null; /* print('Read: $ret'); */ return ret; }
+  _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
diff --git a/lib/src/renderer.dart b/lib/src/renderer.dart
index 198b15b..3a5744d 100644
--- a/lib/src/renderer.dart
+++ b/lib/src/renderer.dart
@@ -214,7 +214,7 @@
     
     if (value == _noSuchProperty) {
       if (!_lenient) 
-        throw _error('Value was missing, variable: ${node.value}', node);
+        throw _error('Value was missing for variable tag: ${node.value}.', node);
     } else {
       var valueString = (value == null) ? '' : value.toString();
       var output = !escape || !_htmlEscapeValues
@@ -257,7 +257,7 @@
     
     } else if (value == _noSuchProperty) {
       if (!_lenient)
-        throw _error('Value was missing, section: ${node.value}', node);
+        throw _error('Value was missing for section tag: ${node.value}.', node);
     
     } else if (value is Function) {
       var context = new _LambdaContext(node, this, isSection: true);
@@ -268,7 +268,7 @@
     } else {
       throw _error('Invalid value type for section, '
         'section: ${node.value}, '
-        'type: ${value.runtimeType}', node);
+        'type: ${value.runtimeType}.', node);
     }
   }
 
@@ -288,7 +288,7 @@
       if (_lenient) {
         _renderSectionWithValue(node, null);
       } else {
-        throw _error('Value was missing, inverse-section: ${node.value}', node);
+        throw _error('Value was missing for inverse section: ${node.value}.', node);
       }
 
      } else if (value is Function) {       
@@ -299,7 +299,7 @@
       throw _error(
         'Invalid value type for inverse section, '
         'section: ${node.value}, '
-        'type: ${value.runtimeType}, ', node);
+        'type: ${value.runtimeType}.', node);
     }
   }
 
diff --git a/lib/src/scanner.dart b/lib/src/scanner.dart
index 3b5e513..31506b5 100644
--- a/lib/src/scanner.dart
+++ b/lib/src/scanner.dart
@@ -54,9 +54,6 @@
 				_templateName, _source, _r.offset);

 		}

 	}

-

-  //FIXME unless in lenient mode only allow spaces.

-	String _readTagWhitespace() => _r.readWhile(_isWhitespace);

 	

 	bool _isWhitespace(int c)

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

@@ -105,7 +102,7 @@
         }			  

 			

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

-        var value = _r.readWhile((c) => c == _SPACE || c == _TAB);

+        var value = _r.readWhile((c) => c == _SPACE || c == _TAB, null); //FIXME remove null.

         _tokens.add(new _Token(_WHITESPACE, value, start, _r.offset));

 			

       //FIXME figure out why this is required

@@ -116,8 +113,8 @@
 			 

 			} else {

         var value = _r.readWhile((c) => c != _openDelimiter

-                                        && c != _EOF

-                                        && c != _NEWLINE);

+                                        && c != _NEWLINE,

+                                        null); //FIXME remove null.

         _tokens.add(new _Token(_TEXT, value, start, _r.offset));

 			}

 		}	

@@ -176,24 +173,32 @@
      _tokens.add(new _Token(_CHANGE_DELIMITER, value, start, _r.offset));

 	}

 

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

+	

 	_scanTagWhitespace() {

 	  const whitepsace = const [_SPACE, _NEWLINE, _RETURN, _TAB];

 	  if (_lenient) {

-	    _r.readWhile(_isWhitespace);	    

+	    _r.readWhile(_isWhitespace, _errorEofInTag);

 	  } else {

-	    _r.readWhile((c) => c == _SPACE);

+	    _r.readWhile((c) => c == _SPACE, _errorEofInTag);

 	    if (_isWhitespace(_peek()))

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

 	  }

 	}

 	

 	String _scanTagIdentifier() {

+	  //FIXME put this in a getter.

+	  var delim = _closeDelimiterInner != null

+        ? _closeDelimiterInner

+	      : _closeDelimiter;

 	  if (_lenient) {

-	    return _closeDelimiterInner != null

-	        ? _r.readWhile((c) => c != _closeDelimiterInner) //FIXME reimplement readWhile to throw error on eof.

-	        : _r.readWhile((c) => c != _closeDelimiter);

+	    return _r.readWhile((c) => c != delim, _errorEofInTag).trim();

 	  } else {

-	    return _r.readWhile(_isAlphanum);

+	    var id = _r.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;

 	  }

 	}

 	

@@ -234,10 +239,8 @@
     _scanTagWhitespace();

  

     var identifier = _scanTagIdentifier();

-

-    var value = identifier.trim();

     

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

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

     

     _scanTagWhitespace();

  

@@ -256,7 +259,7 @@
     var type = sigils[sigil];

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

     

-    _tokens.add(new _Token(type, value, start, _r.offset, indent: indent));

+    _tokens.add(new _Token(type, identifier, start, _r.offset, indent: indent));

   }

 	

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

@@ -279,7 +282,7 @@
   

   _scanTripleMustacheTag(int start) {

     _expect(_OPEN_MUSTACHE);

-    var value = _r.readWhile((c) => c != _CLOSE_MUSTACHE).trim();

+    var value = _r.readWhile((c) => c != _CLOSE_MUSTACHE, _errorEofInTag).trim();

     //FIXME lenient/strict mode identifier parsing.

     _expect(_CLOSE_MUSTACHE);

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

@@ -289,8 +292,8 @@
   

   _scanCommentTag(int start) {

     var value = _closeDelimiterInner != null

-        ? _r.readWhile((c) => c != _closeDelimiterInner).trim()

-        : _r.readWhile((c) => c != _closeDelimiter).trim();

+        ? _r.readWhile((c) => c != _closeDelimiterInner, _errorEofInTag).trim()

+        : _r.readWhile((c) => c != _closeDelimiter, _errorEofInTag).trim();

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

     _expect(_closeDelimiter);

     _tokens.add(new _Token(_COMMENT, value, start, _r.offset));

diff --git a/lib/src/template.dart b/lib/src/template.dart
index 921dc9d..6dbe3b1 100644
--- a/lib/src/template.dart
+++ b/lib/src/template.dart
@@ -71,7 +71,7 @@
     if (templateName != null) list.add(templateName);

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

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

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

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

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

   }

 

diff --git a/lib/src/token.dart b/lib/src/token.dart
index 7bff864..8e2e4dd 100644
--- a/lib/src/token.dart
+++ b/lib/src/token.dart
@@ -9,6 +9,9 @@
   
   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;
   
   toString() => "${_tokenTypeString(type)}: "
diff --git a/test/mustache_test.dart b/test/mustache_test.dart
index 5395336..7ba3216 100644
--- a/test/mustache_test.dart
+++ b/test/mustache_test.dart
@@ -4,10 +4,10 @@
 import 'package:mustache/mustache.dart';
 
 const MISMATCHED_TAG = 'Mismatched tag';
-const UNEXPECTED_EOF = 'Unexpected end of input';
+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 = 'Tag contained invalid characters in name';
+const BAD_TAG_NAME = 'Unless in lenient mode tags may only contain';
 const VALUE_NULL = 'Value was null or missing';
 
 Template parse(String source, {bool lenient: false})
@@ -115,7 +115,7 @@
         "{{\t#foo }} {{ oi }} {{ /foo }}",
         {'foo': [{'oi': 'OI!'}]},
         ' OI! ');
-      
+
       render(
         "{{{ #foo }}} {{{ /foo }}}",
         {'#foo': 1, '/foo': 2},
@@ -147,10 +147,11 @@
         {'foo': true},
         'true');
 
-      render(
-        "{{ > }}",
-        {'>': 'oi'},
-        '');      
+//FIXME empty, or error in strict mode.
+//      render(
+//        "{{ > }}",
+//        {'>': 'oi'},
+//        '');      
     });
 	});
 
@@ -244,14 +245,14 @@
 	group('Invalid format', () {
 		test('Mismatched tag', () {
 			var source = '{{#section}}_{{var}}_{{/notsection}}';
-			var ex = renderFail(source, {"section": {"var": "bob"}});
-			expectFail(ex, 1, 25, 'Mismatched tag');
+			var ex = renderFail(source, {"section": {"var": "bob"}});			
+			expectFail(ex, 1, 22, 'Mismatched tag');
 		});
 
 		test('Unexpected EOF', () {
 			var source = '{{#section}}_{{var}}_{{/section';
 			var ex = renderFail(source, {"section": {"var": "bob"}});
-			expectFail(ex, 1, source.length + 2, UNEXPECTED_EOF);
+			expectFail(ex, 1, source.length, UNEXPECTED_EOF);
 		});
 
 		test('Bad tag name, open section', () {
@@ -351,7 +352,6 @@
           lenient: false);  
       } catch (e) {
         expect(e is TemplateException, isTrue);
-        print(e);
         threw = true;
       }
       expect(threw, isTrue);