Refactor node into seperate classes move render methods into nodes from renderer.
diff --git a/lib/src/lambda_context.dart b/lib/src/lambda_context.dart
index 37056a9..ee5f2f8 100644
--- a/lib/src/lambda_context.dart
+++ b/lib/src/lambda_context.dart
@@ -44,12 +44,14 @@
   String get source {
     _checkClosed();
     
-    var nodes = _node.children;
+    if (_node is! _SectionNode) return '';
+    
+    var nodes = (_node as _SectionNode).children;
     
     if (nodes.isEmpty) return '';
     
-    if (nodes.length == 1 && nodes.first.type == _TEXT)
-      return nodes.first.value;
+    if (nodes.length == 1 && nodes.first is _TextNode)
+      return nodes.first.text;
     
     var source = _renderer._source.substring(
         _node.contentStart, _node.contentEnd);
diff --git a/lib/src/node.dart b/lib/src/node.dart
index 44ab33e..ebd3824 100644
--- a/lib/src/node.dart
+++ b/lib/src/node.dart
@@ -1,18 +1,10 @@
 part of mustache;
 
-class _Node {
+abstract class _Node {
   
-  _Node(this.type, this.value, this.start, this.end, {this.indent});
+  _Node(this.start, this.end);
   
-  _Node.fromToken(_Token token)
-    : type = token.type,
-      value = token.value,
-      start = token.start,
-      end = token.end,
-      indent = token.indent;
-  
-  final int type;
-  final String value;
+  void render(_Renderer renderer);
   
   // 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
@@ -23,11 +15,173 @@
   int contentStart;
   int contentEnd;
   
+}
+
+
+class _TextNode extends _Node {
+  
+  _TextNode(this.text, int start, int end) : super(start, end);
+  
+  final String text;
+  
+  render(_Renderer renderer, {lastNode: false}) {
+    if (text == '') return;
+    if (renderer._indent == null || renderer._indent == '') {
+      renderer._write(text);
+    } else if (lastNode && text.runes.last == _NEWLINE) {
+      var s = text.substring(0, text.length - 1);
+      renderer._write(s.replaceAll('\n', '\n$renderer._indent'));
+      renderer._write('\n');
+    } else {
+      renderer._write(text.replaceAll('\n', '\n$renderer._indent'));
+    }
+  }
+}
+
+class _VariableNode extends _Node {
+  
+  _VariableNode(this.name, int start, int end, this.delimiters,
+      {this.escape: false})
+    : super(start, end);
+  
+  final String name;
+  final String delimiters;
+  final bool escape;
+  
+  render(_Renderer renderer) {
+    
+    var value = renderer._resolveValue(name);
+    
+    if (value is Function) {
+      var context = new _LambdaContext(this, renderer, isSection: false);
+      value = value(context);
+      context.close();
+    }
+    
+    if (value == _noSuchProperty) {
+      if (!renderer._lenient) 
+        throw renderer._error('Value was missing for variable tag: ${name}.', this);
+    } else {
+      var valueString = (value == null) ? '' : value.toString();
+      var output = !escape || !renderer._htmlEscapeValues
+        ? valueString
+        : renderer._htmlEscape(valueString);
+      renderer._write(output);
+    }
+  }
+}
+
+
+class _SectionNode extends _Node {
+  
+  _SectionNode(this.name, int start, int end, this.delimiters,
+      {this.inverse: false})
+    : super(start, end);
+  
+  final String name;
+  final String delimiters;
+  final bool inverse;
+  int contentStart;
+  int contentEnd;
+  final List<_Node> children = <_Node>[];
+  
+  //TODO can probably combine Inv and Normal to shorten.
+  void render(_Renderer renderer) => inverse
+      ? renderInv(renderer)
+      : renderNormal(renderer);
+  
+  void renderNormal(_Renderer renderer) {
+    var value = renderer._resolveValue(name);
+    
+    if (value == null) {
+      // Do nothing.
+    
+    } else if (value is Iterable) {
+      value.forEach((v) => renderer._renderSectionWithValue(this, v));
+    
+    } else if (value is Map) {
+      renderer._renderSectionWithValue(this, value);
+    
+    } else if (value == true) {
+      renderer._renderSectionWithValue(this, value);
+    
+    } else if (value == false) {
+      // Do nothing.
+    
+    } else if (value == _noSuchProperty) {
+      if (!renderer._lenient)
+        throw renderer._error('Value was missing for section tag: ${name}.', this);
+    
+    } else if (value is Function) {
+      var context = new _LambdaContext(this, renderer, isSection: true);
+      var output = value(context);
+      context.close();        
+      renderer._write(output);
+      
+    } else {
+      throw renderer._error('Invalid value type for section, '
+        'section: ${name}, '
+        'type: ${value.runtimeType}.', this);
+    }
+  }
+  
+  void renderInv(_Renderer renderer) {
+    var value = renderer._resolveValue(name);
+    
+    if (value == null) {
+      renderer._renderSectionWithValue(this, null);
+    
+    } else if ((value is Iterable && value.isEmpty) || value == false) {
+      renderer._renderSectionWithValue(this, name);
+    
+    } else if (value == true || value is Map || value is Iterable) {
+      // Do nothing.
+    
+    } else if (value == _noSuchProperty) {
+      if (renderer._lenient) {
+        renderer._renderSectionWithValue(this, null);
+      } else {
+        throw renderer._error('Value was missing for inverse section: ${name}.', this);
+      }
+  
+     } else if (value is Function) {       
+      // Do nothing.
+       //TODO in strict mode should this be an error?
+  
+    } else {
+      throw renderer._error(
+        'Invalid value type for inverse section, '
+        'section: $name, '
+        'type: ${value.runtimeType}.', this);
+    }
+  }
+}
+
+class _PartialNode extends _Node {
+
+  _PartialNode(this.name, int start, int end, this.delimiters, this.indent)
+    : super(start, end);
+  
+  final String name;
+  final String delimiters; //FIXME
+  
   // 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)}';
+  void render(_Renderer renderer) {
+    var partialName = name;
+    _Template template = renderer._partialResolver == null
+        ? null
+        : renderer._partialResolver(partialName);
+    if (template != null) {
+      var r = new _Renderer.partial(renderer, template, this.indent);
+      r.render(); //FIXME Hmm change this.
+    } else if (renderer._lenient) {
+      // do nothing
+    } else {
+      throw renderer._error('Partial not found: $partialName.', this);
+    }
+  }
 }
+
diff --git a/lib/src/parse.dart b/lib/src/parse.dart
index 9858de0..63695c6 100644
--- a/lib/src/parse.dart
+++ b/lib/src/parse.dart
@@ -13,30 +13,44 @@
   tokens = _removeStandaloneWhitespace(tokens);
   tokens = _mergeAdjacentText(tokens);
   
-  var stack = new List<_Node>()..add(new _Node(_OPEN_SECTION, 'root', 0, 0));
+  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, delim, escape: t.type != _UNESC_VARIABLE);
+        stack.last.children.add(n);
+        break;
+
       case _PARTIAL:
-        stack.last.children.add(new _Node.fromToken(t));
+        var n = new _PartialNode(t.value, t.start, t.end, delim, 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 _Node.fromToken(t)..contentStart = t.end;
+        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.value != t.value) {
+        if (stack.last.name != t.value) {
           throw new _TemplateException(
-            "Mismatched tag, expected: '${stack.last.value}', was: '${t.value}'",
+            "Mismatched tag, expected: '${stack.last.name}', was: '${t.value}'",
             templateName, source, t.start);
         }
   
@@ -46,7 +60,7 @@
         break;
       
       case _CHANGE_DELIMITER:
-        stack.last.children.add(new _Node.fromToken(t));
+        delimiters = t.value;
         break;
         
       case _COMMENT:
diff --git a/lib/src/renderer.dart b/lib/src/renderer.dart
index 0387aa0..a6ca9f1 100644
--- a/lib/src/renderer.dart
+++ b/lib/src/renderer.dart
@@ -65,7 +65,7 @@
            source,
            delimiters);
    
-  final _Node _root;
+  final _SectionNode _root;
   final StringSink _sink;
   final _values;
   final List _stack;
@@ -80,7 +80,7 @@
   
   void render() {
     if (_indent == null || _indent == '') {
-     _root.children.forEach(_renderNode);
+     _root.children.forEach((n) => n.render(this));
     } else {
       _renderWithIndent();
     }
@@ -89,68 +89,23 @@
   void _renderWithIndent() {
     // Special case to make sure there is not an extra indent after the last
     // line in the partial file.
-    if (_root.children.isEmpty) return;
+    var nodes = _root.children; 
+    if (nodes.isEmpty) return;
     
     _write(_indent);
     
-    for (int i = 0; i < _root.children.length - 1; i++) {
-      _renderNode(_root.children[i]);
-    }
-    
+    nodes.take(nodes.length - 1).map((n) => n.render(this));
+        
     var node = _root.children.last;
-    if (node.type != _TEXT) {
-      _renderNode(node);
+    if (node is _TextNode) {
+      node.render(this, lastNode: true);
     } else {
-      _renderText(node, lastNode: true);
+      node.render(this);
     }
   }
   
   _write(Object output) => _sink.write(output.toString());
 
-  _renderNode(_Node node) {
-    switch (node.type) {
-      case _TEXT:
-        _renderText(node);
-        break;
-      case _VARIABLE:
-        _renderVariable(node);
-        break;
-      case _UNESC_VARIABLE:
-        _renderVariable(node, escape: false);
-        break;
-      case _OPEN_SECTION:
-        _renderSection(node);
-        break;
-      case _OPEN_INV_SECTION:
-        _renderInvSection(node);
-        break;
-      case _PARTIAL:
-        _renderPartial(node);
-        break;
-      case _COMMENT:
-        break; // Do nothing.
-      case _CHANGE_DELIMITER:
-        _delimiters = node.value;
-        break;
-      default:
-        throw new UnimplementedError();
-    }
-  }
-
-  _renderText(_Node node, {bool lastNode: false}) {
-    var s = node.value;
-    if (s == '') return;
-    if (_indent == null || _indent == '') {
-      _write(s);
-    } else if (lastNode && s.runes.last == _NEWLINE) {
-      s = s.substring(0, s.length - 1);
-      _write(s.replaceAll('\n', '\n$_indent'));
-      _write('\n');
-    } else {
-      _write(s.replaceAll('\n', '\n$_indent'));
-    }
-  }
-
   // Walks up the stack looking for the variable.
   // Handles dotted names of the form "a.b.c".
   _resolveValue(String name) {
@@ -205,30 +160,9 @@
     return invocation.reflectee;
   }
 
-  _renderVariable(_Node node, {bool escape : true}) {
-    var value = _resolveValue(node.value);
-    
-    if (value is Function) {
-      var context = new _LambdaContext(node, this, isSection: false);
-      value = value(context);
-      context.close();
-    }
-    
-    if (value == _noSuchProperty) {
-      if (!_lenient) 
-        throw _error('Value was missing for variable tag: ${node.value}.', node);
-    } else {
-      var valueString = (value == null) ? '' : value.toString();
-      var output = !escape || !_htmlEscapeValues
-        ? valueString
-        : _htmlEscape(valueString);
-      _write(output);
-    }
-  }
-
-  _renderSectionWithValue(node, value) {
+  void _renderSectionWithValue(node, value) {
     _stack.add(value);
-    node.children.forEach(_renderNode);
+    node.children.forEach((n) => n.render(this));
     _stack.removeLast();
   }
 
@@ -238,87 +172,6 @@
     renderer.render();
     return sink.toString();
   }
-  
-  _renderSection(_Node node) {
-    var value = _resolveValue(node.value);
-    
-    if (value == null) {
-      // Do nothing.
-    
-    } else if (value is Iterable) {
-      value.forEach((v) => _renderSectionWithValue(node, v));
-    
-    } else if (value is Map) {
-      _renderSectionWithValue(node, value);
-    
-    } else if (value == true) {
-      _renderSectionWithValue(node, value);
-    
-    } else if (value == false) {
-      // Do nothing.
-    
-    } else if (value == _noSuchProperty) {
-      if (!_lenient)
-        throw _error('Value was missing for section tag: ${node.value}.', node);
-    
-    } else if (value is Function) {
-      var context = new _LambdaContext(node, this, isSection: true);
-      var output = value(context);
-      context.close();        
-      _write(output);
-      
-    } else {
-      throw _error('Invalid value type for section, '
-        'section: ${node.value}, '
-        'type: ${value.runtimeType}.', node);
-    }
-  }
-
-  _renderInvSection(node) {
-    var value = _resolveValue(node.value);
-    
-    if (value == null) {
-      _renderSectionWithValue(node, null);
-    
-    } else if ((value is Iterable && value.isEmpty) || value == false) {
-      _renderSectionWithValue(node, value);
-    
-    } else if (value == true || value is Map || value is Iterable) {
-      // Do nothing.
-    
-    } else if (value == _noSuchProperty) {
-      if (_lenient) {
-        _renderSectionWithValue(node, null);
-      } else {
-        throw _error('Value was missing for inverse section: ${node.value}.', node);
-      }
-
-     } else if (value is Function) {       
-      // Do nothing.
-       //TODO in strict mode should this be an error?
-
-    } else {
-      throw _error(
-        'Invalid value type for inverse section, '
-        'section: ${node.value}, '
-        'type: ${value.runtimeType}.', node);
-    }
-  }
-
-  _renderPartial(_Node node) {
-    var partialName = node.value;
-    _Template template = _partialResolver == null
-        ? null
-        : _partialResolver(partialName);
-    if (template != null) {
-      var renderer = new _Renderer.partial(this, template, node.indent);
-      renderer.render();      
-    } else if (_lenient) {
-      // do nothing
-    } else {
-      throw _error('Partial not found: $partialName.', node);
-    }
-  }
 
   static const Map<String,String> _htmlEscapeMap = const {
     _AMP: '&amp;',