text: var Pathology = {
    evaluate: function(xpathExpression, context, nsResolver, resultType, result) {
      result = result || new Pathology.XPathResult(resultType);
      var expression = Pathology.XPathParser.parse(xpathExpression);
      expression.evaluate(context, context, resultType, result);
      return result;
    },
  
    atomize: function(expression, context, root) {
      var result = expression.evaluate(context, root);
      if (result && result.atomize) result = result.atomize();
      return result;
    },
  
    array: function(list) {
      if (!list) return [];
      var array = [], i = list.length;
      while (i--) array[i] = list[i];
      return array;
    },
  
    indexOf: function(list, item) {
      if (list.indexOf) return list.indexOf(item);
      for (var i = 0, n = list.length; i < n; i++) {
        if (list[i] === item) return i;
      }
      return -1;
    }
  };
  
  if (typeof XPathResult === 'undefined') {
    XPathResult = {
      ANY_TYPE:                     0,
      NUMBER_TYPE:                  1,
      STRING_TYPE:                  2,
      BOOLEAN_TYPE:                 3,
      UNORDERED_NODE_ITERATOR_TYPE: 4,
      ORDERED_NODE_ITERATOR_TYPE:   5,
      UNORDERED_NODE_SNAPSHOT_TYPE: 6,
      ORDERED_NODE_SNAPSHOT_TYPE:   7,
      ANY_UNORDERED_NODE_TYPE:      8,
      FIRST_ORDERED_NODE_TYPE:      9
    };
  }
  
  if (typeof document.evaluate === 'undefined') {
    document.evaluate = function() {
      return Pathology.evaluate.apply(Pathology, arguments);
    };
  }
  
  
  Pathology.XPathResult = function(resultType) {
    this._type  = resultType;
    this._nodes = [];
    this._index = 0;
  };
  
  Pathology.XPathResult.prototype.push = function(node) {
    if (this._type !== 0 && node.nodeType !== this._type) return;
    if (Pathology.indexOf(this._nodes, node) >= 0) return;
    this._nodes.push(node);
  };
  
  Pathology.XPathResult.prototype.iterateNext = function() {
    var node = this._nodes[this._index];
    if (!node) return null;
    this._index += 1;
    return node;
  };
  
  Pathology.XPathResult.prototype.atomize = function() {
    if (this._nodes.length === 0) return null;
    if (this._nodes.length === 1) {
      var node = this._nodes[0];
      if (node.nodeValue === undefined || node.nodeValue === null) return node;
      return node.nodeValue;
    } else {
      var nodes = [];
      for (var i = 0, n = this._nodes.length; i < n; i++) nodes.push(this._nodes[i].nodeValue);
      return nodes;
    }
  };
  
  Pathology.XPathResult.prototype.makeString = function() {
    var first = this._nodes[0];
    if (!first) return '';
  
    switch (first.nodeType) {
      case XPathResult.STRING_TYPE:
        return this.atomize();
  
      case XPathResult.BOOLEAN_TYPE:
        var parts = [];
        for (var i = 0, n = this._nodes.length; i < n; i++)
          parts.push(this._nodes[i].nodeValue);
        return parts.join('');
  
      default:
        var result = document.evaluate('//text()', first, null, XPathResult.ANY_TYPE, null);
        return result.makeString();
    }
  };
  
  
  (function() {
    var extend = function (destination, source) {
      if (!destination || !source) return destination;
      for (var key in source) {
        if (destination[key] !== source[key])
          destination[key] = source[key];
      }
      return destination;
    };
  
    var find = function (root, objectName) {
      var parts = objectName.split('.'),
          part;
  
      while (part = parts.shift()) {
        root = root[part];
        if (root === undefined)
          throw new Error('Cannot find object named ' + objectName);
      }
      return root;
    };
  
    var formatError = function (error) {
      var lines  = error.input.split(/\n/g),
          lineNo = 0,
          offset = 0;
  
      while (offset < error.offset + 1) {
        offset += lines[lineNo].length + 1;
        lineNo += 1;
      }
      var message = 'Line ' + lineNo + ': expected ' + error.expected + '\n',
          line    = lines[lineNo - 1];
  
      message += line + '\n';
      offset  -= line.length + 1;
  
      while (offset < error.offset) {
        message += ' ';
        offset  += 1;
      }
      return message + '^';
    };
  
    var Grammar = {
      __consume__union_expression: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["union_expression"] = this._nodeCache["union_expression"] || {};
        var cached = this._nodeCache["union_expression"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset, elements0 = [], labelled0 = {}, text0 = "";
        var address1 = null;
        address1 = this.__consume__location_path();
        if (address1) {
          elements0.push(address1);
          text0 += address1.textValue;
          labelled0.head = address1;
          var address2 = null;
          var remaining0 = 0, index2 = this._offset, elements1 = [], text1 = "", address3 = true;
          while (address3) {
            var index3 = this._offset, elements2 = [], labelled1 = {}, text2 = "";
            var address4 = null;
            address4 = this.__consume__space();
            if (address4) {
              elements2.push(address4);
              text2 += address4.textValue;
              labelled1.space = address4;
              var address5 = null;
              var slice0 = null;
              if (this._input.length > this._offset) {
                slice0 = this._input.substring(this._offset, this._offset + 1);
              } else {
                slice0 = null;
              }
              if (slice0 === "|") {
                var klass0 = this.constructor.SyntaxNode;
                var type0 = null;
                address5 = new klass0("|", this._offset, []);
                if (typeof type0 === "object") {
                  extend(address5, type0);
                }
                this._offset += 1;
              } else {
                address5 = null;
                var slice1 = null;
                if (this._input.length > this._offset) {
                  slice1 = this._input.substring(this._offset, this._offset + 1);
                } else {
                  slice1 = null;
                }
                if (!this.error || this.error.offset <= this._offset) {
                  this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"|\""};
                }
              }
              if (address5) {
                elements2.push(address5);
                text2 += address5.textValue;
                var address6 = null;
                address6 = this.__consume__space();
                if (address6) {
                  elements2.push(address6);
                  text2 += address6.textValue;
                  labelled1.space = address6;
                  var address7 = null;
                  address7 = this.__consume__location_path();
                  if (address7) {
                    elements2.push(address7);
                    text2 += address7.textValue;
                    labelled1.location_path = address7;
                  } else {
                    elements2 = null;
                    this._offset = index3;
                  }
                } else {
                  elements2 = null;
                  this._offset = index3;
                }
              } else {
                elements2 = null;
                this._offset = index3;
              }
            } else {
              elements2 = null;
              this._offset = index3;
            }
            if (elements2) {
              this._offset = index3;
              var klass1 = this.constructor.SyntaxNode;
              var type1 = null;
              address3 = new klass1(text2, this._offset, elements2, labelled1);
              if (typeof type1 === "object") {
                extend(address3, type1);
              }
              this._offset += text2.length;
            } else {
              address3 = null;
            }
            if (address3) {
              elements1.push(address3);
              text1 += address3.textValue;
              remaining0 -= 1;
            }
          }
          if (remaining0 <= 0) {
            this._offset = index2;
            var klass2 = this.constructor.SyntaxNode;
            var type2 = null;
            address2 = new klass2(text1, this._offset, elements1);
            if (typeof type2 === "object") {
              extend(address2, type2);
            }
            this._offset += text1.length;
          } else {
            address2 = null;
          }
          if (address2) {
            elements0.push(address2);
            text0 += address2.textValue;
            labelled0.rest = address2;
          } else {
            elements0 = null;
            this._offset = index1;
          }
        } else {
          elements0 = null;
          this._offset = index1;
        }
        if (elements0) {
          this._offset = index1;
          var klass3 = this.constructor.SyntaxNode;
          var type3 = find(this.constructor, "Union");
          address0 = new klass3(text0, this._offset, elements0, labelled0);
          if (typeof type3 === "object") {
            extend(address0, type3);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        return this._nodeCache["union_expression"][index0] = address0;
      },
      __consume__location_path: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["location_path"] = this._nodeCache["location_path"] || {};
        var cached = this._nodeCache["location_path"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset, elements0 = [], labelled0 = {}, text0 = "";
        var address1 = null;
        address1 = this.__consume__location_step();
        if (address1) {
          elements0.push(address1);
          text0 += address1.textValue;
          labelled0.head = address1;
          var address2 = null;
          var remaining0 = 0, index2 = this._offset, elements1 = [], text1 = "", address3 = true;
          while (address3) {
            address3 = this.__consume__location_step();
            if (address3) {
              elements1.push(address3);
              text1 += address3.textValue;
              remaining0 -= 1;
            }
          }
          if (remaining0 <= 0) {
            this._offset = index2;
            var klass0 = this.constructor.SyntaxNode;
            var type0 = null;
            address2 = new klass0(text1, this._offset, elements1);
            if (typeof type0 === "object") {
              extend(address2, type0);
            }
            this._offset += text1.length;
          } else {
            address2 = null;
          }
          if (address2) {
            elements0.push(address2);
            text0 += address2.textValue;
            labelled0.rest = address2;
          } else {
            elements0 = null;
            this._offset = index1;
          }
        } else {
          elements0 = null;
          this._offset = index1;
        }
        if (elements0) {
          this._offset = index1;
          var klass1 = this.constructor.SyntaxNode;
          var type1 = find(this.constructor, "LocationPath");
          address0 = new klass1(text0, this._offset, elements0, labelled0);
          if (typeof type1 === "object") {
            extend(address0, type1);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        return this._nodeCache["location_path"][index0] = address0;
      },
      __consume__location_step: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["location_step"] = this._nodeCache["location_step"] || {};
        var cached = this._nodeCache["location_step"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset, elements0 = [], labelled0 = {}, text0 = "";
        var address1 = null;
        var index2 = this._offset;
        var slice0 = null;
        if (this._input.length > this._offset) {
          slice0 = this._input.substring(this._offset, this._offset + 1);
        } else {
          slice0 = null;
        }
        if (slice0 === "/") {
          var klass0 = this.constructor.SyntaxNode;
          var type0 = null;
          address1 = new klass0("/", this._offset, []);
          if (typeof type0 === "object") {
            extend(address1, type0);
          }
          this._offset += 1;
        } else {
          address1 = null;
          var slice1 = null;
          if (this._input.length > this._offset) {
            slice1 = this._input.substring(this._offset, this._offset + 1);
          } else {
            slice1 = null;
          }
          if (!this.error || this.error.offset <= this._offset) {
            this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"/\""};
          }
        }
        if (address1) {
        } else {
          this._offset = index2;
          var klass1 = this.constructor.SyntaxNode;
          var type1 = null;
          address1 = new klass1("", this._offset, []);
          if (typeof type1 === "object") {
            extend(address1, type1);
          }
          this._offset += 0;
        }
        if (address1) {
          elements0.push(address1);
          text0 += address1.textValue;
          var address2 = null;
          var index3 = this._offset;
          var index4 = this._offset, elements1 = [], labelled1 = {}, text1 = "";
          var address3 = null;
          address3 = this.__consume__axis();
          if (address3) {
            elements1.push(address3);
            text1 += address3.textValue;
            labelled1.axis = address3;
            var address4 = null;
            var index5 = this._offset;
            address4 = this.__consume__node_test();
            if (address4) {
            } else {
              this._offset = index5;
              var klass2 = this.constructor.SyntaxNode;
              var type2 = null;
              address4 = new klass2("", this._offset, []);
              if (typeof type2 === "object") {
                extend(address4, type2);
              }
              this._offset += 0;
            }
            if (address4) {
              elements1.push(address4);
              text1 += address4.textValue;
              labelled1.test = address4;
            } else {
              elements1 = null;
              this._offset = index4;
            }
          } else {
            elements1 = null;
            this._offset = index4;
          }
          if (elements1) {
            this._offset = index4;
            var klass3 = this.constructor.SyntaxNode;
            var type3 = null;
            address2 = new klass3(text1, this._offset, elements1, labelled1);
            if (typeof type3 === "object") {
              extend(address2, type3);
            }
            this._offset += text1.length;
          } else {
            address2 = null;
          }
          if (address2) {
          } else {
            this._offset = index3;
            var index6 = this._offset, elements2 = [], labelled2 = {}, text2 = "";
            var address5 = null;
            var index7 = this._offset;
            address5 = this.__consume__axis();
            if (address5) {
            } else {
              this._offset = index7;
              var klass4 = this.constructor.SyntaxNode;
              var type4 = null;
              address5 = new klass4("", this._offset, []);
              if (typeof type4 === "object") {
                extend(address5, type4);
              }
              this._offset += 0;
            }
            if (address5) {
              elements2.push(address5);
              text2 += address5.textValue;
              labelled2.axis = address5;
              var address6 = null;
              address6 = this.__consume__node_test();
              if (address6) {
                elements2.push(address6);
                text2 += address6.textValue;
                labelled2.test = address6;
              } else {
                elements2 = null;
                this._offset = index6;
              }
            } else {
              elements2 = null;
              this._offset = index6;
            }
            if (elements2) {
              this._offset = index6;
              var klass5 = this.constructor.SyntaxNode;
              var type5 = null;
              address2 = new klass5(text2, this._offset, elements2, labelled2);
              if (typeof type5 === "object") {
                extend(address2, type5);
              }
              this._offset += text2.length;
            } else {
              address2 = null;
            }
            if (address2) {
            } else {
              this._offset = index3;
            }
          }
          if (address2) {
            elements0.push(address2);
            text0 += address2.textValue;
            labelled0.selector = address2;
            var address7 = null;
            var remaining0 = 0, index8 = this._offset, elements3 = [], text3 = "", address8 = true;
            while (address8) {
              address8 = this.__consume__node_predicate();
              if (address8) {
                elements3.push(address8);
                text3 += address8.textValue;
                remaining0 -= 1;
              }
            }
            if (remaining0 <= 0) {
              this._offset = index8;
              var klass6 = this.constructor.SyntaxNode;
              var type6 = null;
              address7 = new klass6(text3, this._offset, elements3);
              if (typeof type6 === "object") {
                extend(address7, type6);
              }
              this._offset += text3.length;
            } else {
              address7 = null;
            }
            if (address7) {
              elements0.push(address7);
              text0 += address7.textValue;
              labelled0.predicates = address7;
            } else {
              elements0 = null;
              this._offset = index1;
            }
          } else {
            elements0 = null;
            this._offset = index1;
          }
        } else {
          elements0 = null;
          this._offset = index1;
        }
        if (elements0) {
          this._offset = index1;
          var klass7 = this.constructor.SyntaxNode;
          var type7 = find(this.constructor, "LocationStep");
          address0 = new klass7(text0, this._offset, elements0, labelled0);
          if (typeof type7 === "object") {
            extend(address0, type7);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        return this._nodeCache["location_step"][index0] = address0;
      },
      __consume__axis: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["axis"] = this._nodeCache["axis"] || {};
        var cached = this._nodeCache["axis"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset;
        var index2 = this._offset, elements0 = [], labelled0 = {}, text0 = "";
        var address1 = null;
        var remaining0 = 1, index3 = this._offset, elements1 = [], text1 = "", address2 = true;
        while (address2) {
          var slice0 = null;
          if (this._input.length > this._offset) {
            slice0 = this._input.substring(this._offset, this._offset + 1);
          } else {
            slice0 = null;
          }
          if (slice0 && /^[a-z\-]/.test(slice0)) {
            var klass0 = this.constructor.SyntaxNode;
            var type0 = null;
            address2 = new klass0(slice0, this._offset, []);
            if (typeof type0 === "object") {
              extend(address2, type0);
            }
            this._offset += 1;
          } else {
            address2 = null;
            var slice1 = null;
            if (this._input.length > this._offset) {
              slice1 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice1 = null;
            }
            if (!this.error || this.error.offset <= this._offset) {
              this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "[a-z\\-]"};
            }
          }
          if (address2) {
            elements1.push(address2);
            text1 += address2.textValue;
            remaining0 -= 1;
          }
        }
        if (remaining0 <= 0) {
          this._offset = index3;
          var klass1 = this.constructor.SyntaxNode;
          var type1 = null;
          address1 = new klass1(text1, this._offset, elements1);
          if (typeof type1 === "object") {
            extend(address1, type1);
          }
          this._offset += text1.length;
        } else {
          address1 = null;
        }
        if (address1) {
          elements0.push(address1);
          text0 += address1.textValue;
          labelled0.axis_name = address1;
          var address3 = null;
          var slice2 = null;
          if (this._input.length > this._offset) {
            slice2 = this._input.substring(this._offset, this._offset + 2);
          } else {
            slice2 = null;
          }
          if (slice2 === "::") {
            var klass2 = this.constructor.SyntaxNode;
            var type2 = null;
            address3 = new klass2("::", this._offset, []);
            if (typeof type2 === "object") {
              extend(address3, type2);
            }
            this._offset += 2;
          } else {
            address3 = null;
            var slice3 = null;
            if (this._input.length > this._offset) {
              slice3 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice3 = null;
            }
            if (!this.error || this.error.offset <= this._offset) {
              this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"::\""};
            }
          }
          if (address3) {
            elements0.push(address3);
            text0 += address3.textValue;
          } else {
            elements0 = null;
            this._offset = index2;
          }
        } else {
          elements0 = null;
          this._offset = index2;
        }
        if (elements0) {
          this._offset = index2;
          var klass3 = this.constructor.SyntaxNode;
          var type3 = null;
          address0 = new klass3(text0, this._offset, elements0, labelled0);
          if (typeof type3 === "object") {
            extend(address0, type3);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        if (address0) {
        } else {
          this._offset = index1;
          address0 = this.__consume__axis_shorthand();
          if (address0) {
          } else {
            this._offset = index1;
          }
        }
        return this._nodeCache["axis"][index0] = address0;
      },
      __consume__axis_shorthand: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["axis_shorthand"] = this._nodeCache["axis_shorthand"] || {};
        var cached = this._nodeCache["axis_shorthand"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset;
        var slice0 = null;
        if (this._input.length > this._offset) {
          slice0 = this._input.substring(this._offset, this._offset + 1);
        } else {
          slice0 = null;
        }
        if (slice0 === "@") {
          var klass0 = this.constructor.SyntaxNode;
          var type0 = null;
          address0 = new klass0("@", this._offset, []);
          if (typeof type0 === "object") {
            extend(address0, type0);
          }
          this._offset += 1;
        } else {
          address0 = null;
          var slice1 = null;
          if (this._input.length > this._offset) {
            slice1 = this._input.substring(this._offset, this._offset + 1);
          } else {
            slice1 = null;
          }
          if (!this.error || this.error.offset <= this._offset) {
            this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"@\""};
          }
        }
        if (address0) {
        } else {
          this._offset = index1;
          var slice2 = null;
          if (this._input.length > this._offset) {
            slice2 = this._input.substring(this._offset, this._offset + 2);
          } else {
            slice2 = null;
          }
          if (slice2 === "..") {
            var klass1 = this.constructor.SyntaxNode;
            var type1 = null;
            address0 = new klass1("..", this._offset, []);
            if (typeof type1 === "object") {
              extend(address0, type1);
            }
            this._offset += 2;
          } else {
            address0 = null;
            var slice3 = null;
            if (this._input.length > this._offset) {
              slice3 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice3 = null;
            }
            if (!this.error || this.error.offset <= this._offset) {
              this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"..\""};
            }
          }
          if (address0) {
          } else {
            this._offset = index1;
            var slice4 = null;
            if (this._input.length > this._offset) {
              slice4 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice4 = null;
            }
            if (slice4 === ".") {
              var klass2 = this.constructor.SyntaxNode;
              var type2 = null;
              address0 = new klass2(".", this._offset, []);
              if (typeof type2 === "object") {
                extend(address0, type2);
              }
              this._offset += 1;
            } else {
              address0 = null;
              var slice5 = null;
              if (this._input.length > this._offset) {
                slice5 = this._input.substring(this._offset, this._offset + 1);
              } else {
                slice5 = null;
              }
              if (!this.error || this.error.offset <= this._offset) {
                this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\".\""};
              }
            }
            if (address0) {
            } else {
              this._offset = index1;
              var slice6 = null;
              if (this._input.length > this._offset) {
                slice6 = this._input.substring(this._offset, this._offset + 1);
              } else {
                slice6 = null;
              }
              if (slice6 === "/") {
                var klass3 = this.constructor.SyntaxNode;
                var type3 = null;
                address0 = new klass3("/", this._offset, []);
                if (typeof type3 === "object") {
                  extend(address0, type3);
                }
                this._offset += 1;
              } else {
                address0 = null;
                var slice7 = null;
                if (this._input.length > this._offset) {
                  slice7 = this._input.substring(this._offset, this._offset + 1);
                } else {
                  slice7 = null;
                }
                if (!this.error || this.error.offset <= this._offset) {
                  this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"/\""};
                }
              }
              if (address0) {
              } else {
                this._offset = index1;
              }
            }
          }
        }
        return this._nodeCache["axis_shorthand"][index0] = address0;
      },
      __consume__node_test: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["node_test"] = this._nodeCache["node_test"] || {};
        var cached = this._nodeCache["node_test"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset, elements0 = [], labelled0 = {}, text0 = "";
        var address1 = null;
        var index2 = this._offset;
        address1 = this.__consume__node_condition();
        if (address1) {
        } else {
          this._offset = index2;
          address1 = this.__consume__node_name();
          if (address1) {
          } else {
            this._offset = index2;
            var slice0 = null;
            if (this._input.length > this._offset) {
              slice0 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice0 = null;
            }
            if (slice0 === "*") {
              var klass0 = this.constructor.SyntaxNode;
              var type0 = null;
              address1 = new klass0("*", this._offset, []);
              if (typeof type0 === "object") {
                extend(address1, type0);
              }
              this._offset += 1;
            } else {
              address1 = null;
              var slice1 = null;
              if (this._input.length > this._offset) {
                slice1 = this._input.substring(this._offset, this._offset + 1);
              } else {
                slice1 = null;
              }
              if (!this.error || this.error.offset <= this._offset) {
                this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"*\""};
              }
            }
            if (address1) {
            } else {
              this._offset = index2;
            }
          }
        }
        if (address1) {
          elements0.push(address1);
          text0 += address1.textValue;
          var address2 = null;
          var index3 = this._offset;
          address2 = this.__consume__subscript();
          if (address2) {
          } else {
            this._offset = index3;
            var klass1 = this.constructor.SyntaxNode;
            var type1 = null;
            address2 = new klass1("", this._offset, []);
            if (typeof type1 === "object") {
              extend(address2, type1);
            }
            this._offset += 0;
          }
          if (address2) {
            elements0.push(address2);
            text0 += address2.textValue;
            labelled0.subscript = address2;
          } else {
            elements0 = null;
            this._offset = index1;
          }
        } else {
          elements0 = null;
          this._offset = index1;
        }
        if (elements0) {
          this._offset = index1;
          var klass2 = this.constructor.SyntaxNode;
          var type2 = find(this.constructor, "NodeTest");
          address0 = new klass2(text0, this._offset, elements0, labelled0);
          if (typeof type2 === "object") {
            extend(address0, type2);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        return this._nodeCache["node_test"][index0] = address0;
      },
      __consume__node_condition: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["node_condition"] = this._nodeCache["node_condition"] || {};
        var cached = this._nodeCache["node_condition"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset, elements0 = [], labelled0 = {}, text0 = "";
        var address1 = null;
        var remaining0 = 1, index2 = this._offset, elements1 = [], text1 = "", address2 = true;
        while (address2) {
          var slice0 = null;
          if (this._input.length > this._offset) {
            slice0 = this._input.substring(this._offset, this._offset + 1);
          } else {
            slice0 = null;
          }
          if (slice0 && /^[a-z\-]/.test(slice0)) {
            var klass0 = this.constructor.SyntaxNode;
            var type0 = null;
            address2 = new klass0(slice0, this._offset, []);
            if (typeof type0 === "object") {
              extend(address2, type0);
            }
            this._offset += 1;
          } else {
            address2 = null;
            var slice1 = null;
            if (this._input.length > this._offset) {
              slice1 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice1 = null;
            }
            if (!this.error || this.error.offset <= this._offset) {
              this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "[a-z\\-]"};
            }
          }
          if (address2) {
            elements1.push(address2);
            text1 += address2.textValue;
            remaining0 -= 1;
          }
        }
        if (remaining0 <= 0) {
          this._offset = index2;
          var klass1 = this.constructor.SyntaxNode;
          var type1 = null;
          address1 = new klass1(text1, this._offset, elements1);
          if (typeof type1 === "object") {
            extend(address1, type1);
          }
          this._offset += text1.length;
        } else {
          address1 = null;
        }
        if (address1) {
          elements0.push(address1);
          text0 += address1.textValue;
          labelled0.condition_name = address1;
          var address3 = null;
          var slice2 = null;
          if (this._input.length > this._offset) {
            slice2 = this._input.substring(this._offset, this._offset + 2);
          } else {
            slice2 = null;
          }
          if (slice2 === "()") {
            var klass2 = this.constructor.SyntaxNode;
            var type2 = null;
            address3 = new klass2("()", this._offset, []);
            if (typeof type2 === "object") {
              extend(address3, type2);
            }
            this._offset += 2;
          } else {
            address3 = null;
            var slice3 = null;
            if (this._input.length > this._offset) {
              slice3 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice3 = null;
            }
            if (!this.error || this.error.offset <= this._offset) {
              this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"()\""};
            }
          }
          if (address3) {
            elements0.push(address3);
            text0 += address3.textValue;
          } else {
            elements0 = null;
            this._offset = index1;
          }
        } else {
          elements0 = null;
          this._offset = index1;
        }
        if (elements0) {
          this._offset = index1;
          var klass3 = this.constructor.SyntaxNode;
          var type3 = null;
          address0 = new klass3(text0, this._offset, elements0, labelled0);
          if (typeof type3 === "object") {
            extend(address0, type3);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        return this._nodeCache["node_condition"][index0] = address0;
      },
      __consume__node_name: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["node_name"] = this._nodeCache["node_name"] || {};
        var cached = this._nodeCache["node_name"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var remaining0 = 1, index1 = this._offset, elements0 = [], text0 = "", address1 = true;
        while (address1) {
          var slice0 = null;
          if (this._input.length > this._offset) {
            slice0 = this._input.substring(this._offset, this._offset + 1);
          } else {
            slice0 = null;
          }
          if (slice0 && /^[A-Za-z0-9\-]/.test(slice0)) {
            var klass0 = this.constructor.SyntaxNode;
            var type0 = null;
            address1 = new klass0(slice0, this._offset, []);
            if (typeof type0 === "object") {
              extend(address1, type0);
            }
            this._offset += 1;
          } else {
            address1 = null;
            var slice1 = null;
            if (this._input.length > this._offset) {
              slice1 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice1 = null;
            }
            if (!this.error || this.error.offset <= this._offset) {
              this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "[A-Za-z0-9\\-]"};
            }
          }
          if (address1) {
            elements0.push(address1);
            text0 += address1.textValue;
            remaining0 -= 1;
          }
        }
        if (remaining0 <= 0) {
          this._offset = index1;
          var klass1 = this.constructor.SyntaxNode;
          var type1 = null;
          address0 = new klass1(text0, this._offset, elements0);
          if (typeof type1 === "object") {
            extend(address0, type1);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        return this._nodeCache["node_name"][index0] = address0;
      },
      __consume__subscript: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["subscript"] = this._nodeCache["subscript"] || {};
        var cached = this._nodeCache["subscript"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset, elements0 = [], labelled0 = {}, text0 = "";
        var address1 = null;
        var slice0 = null;
        if (this._input.length > this._offset) {
          slice0 = this._input.substring(this._offset, this._offset + 1);
        } else {
          slice0 = null;
        }
        if (slice0 === "[") {
          var klass0 = this.constructor.SyntaxNode;
          var type0 = null;
          address1 = new klass0("[", this._offset, []);
          if (typeof type0 === "object") {
            extend(address1, type0);
          }
          this._offset += 1;
        } else {
          address1 = null;
          var slice1 = null;
          if (this._input.length > this._offset) {
            slice1 = this._input.substring(this._offset, this._offset + 1);
          } else {
            slice1 = null;
          }
          if (!this.error || this.error.offset <= this._offset) {
            this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"[\""};
          }
        }
        if (address1) {
          elements0.push(address1);
          text0 += address1.textValue;
          var address2 = null;
          address2 = this.__consume__space();
          if (address2) {
            elements0.push(address2);
            text0 += address2.textValue;
            labelled0.space = address2;
            var address3 = null;
            address3 = this.__consume__integer();
            if (address3) {
              elements0.push(address3);
              text0 += address3.textValue;
              labelled0.integer = address3;
              var address4 = null;
              address4 = this.__consume__space();
              if (address4) {
                elements0.push(address4);
                text0 += address4.textValue;
                labelled0.space = address4;
                var address5 = null;
                var slice2 = null;
                if (this._input.length > this._offset) {
                  slice2 = this._input.substring(this._offset, this._offset + 1);
                } else {
                  slice2 = null;
                }
                if (slice2 === "]") {
                  var klass1 = this.constructor.SyntaxNode;
                  var type1 = null;
                  address5 = new klass1("]", this._offset, []);
                  if (typeof type1 === "object") {
                    extend(address5, type1);
                  }
                  this._offset += 1;
                } else {
                  address5 = null;
                  var slice3 = null;
                  if (this._input.length > this._offset) {
                    slice3 = this._input.substring(this._offset, this._offset + 1);
                  } else {
                    slice3 = null;
                  }
                  if (!this.error || this.error.offset <= this._offset) {
                    this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"]\""};
                  }
                }
                if (address5) {
                  elements0.push(address5);
                  text0 += address5.textValue;
                } else {
                  elements0 = null;
                  this._offset = index1;
                }
              } else {
                elements0 = null;
                this._offset = index1;
              }
            } else {
              elements0 = null;
              this._offset = index1;
            }
          } else {
            elements0 = null;
            this._offset = index1;
          }
        } else {
          elements0 = null;
          this._offset = index1;
        }
        if (elements0) {
          this._offset = index1;
          var klass2 = this.constructor.SyntaxNode;
          var type2 = null;
          address0 = new klass2(text0, this._offset, elements0, labelled0);
          if (typeof type2 === "object") {
            extend(address0, type2);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        return this._nodeCache["subscript"][index0] = address0;
      },
      __consume__node_predicate: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["node_predicate"] = this._nodeCache["node_predicate"] || {};
        var cached = this._nodeCache["node_predicate"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset, elements0 = [], labelled0 = {}, text0 = "";
        var address1 = null;
        var slice0 = null;
        if (this._input.length > this._offset) {
          slice0 = this._input.substring(this._offset, this._offset + 1);
        } else {
          slice0 = null;
        }
        if (slice0 === "[") {
          var klass0 = this.constructor.SyntaxNode;
          var type0 = null;
          address1 = new klass0("[", this._offset, []);
          if (typeof type0 === "object") {
            extend(address1, type0);
          }
          this._offset += 1;
        } else {
          address1 = null;
          var slice1 = null;
          if (this._input.length > this._offset) {
            slice1 = this._input.substring(this._offset, this._offset + 1);
          } else {
            slice1 = null;
          }
          if (!this.error || this.error.offset <= this._offset) {
            this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"[\""};
          }
        }
        if (address1) {
          elements0.push(address1);
          text0 += address1.textValue;
          var address2 = null;
          address2 = this.__consume__expression();
          if (address2) {
            elements0.push(address2);
            text0 += address2.textValue;
            labelled0.expression = address2;
            var address3 = null;
            var slice2 = null;
            if (this._input.length > this._offset) {
              slice2 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice2 = null;
            }
            if (slice2 === "]") {
              var klass1 = this.constructor.SyntaxNode;
              var type1 = null;
              address3 = new klass1("]", this._offset, []);
              if (typeof type1 === "object") {
                extend(address3, type1);
              }
              this._offset += 1;
            } else {
              address3 = null;
              var slice3 = null;
              if (this._input.length > this._offset) {
                slice3 = this._input.substring(this._offset, this._offset + 1);
              } else {
                slice3 = null;
              }
              if (!this.error || this.error.offset <= this._offset) {
                this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"]\""};
              }
            }
            if (address3) {
              elements0.push(address3);
              text0 += address3.textValue;
              var address4 = null;
              var index2 = this._offset;
              address4 = this.__consume__subscript();
              if (address4) {
              } else {
                this._offset = index2;
                var klass2 = this.constructor.SyntaxNode;
                var type2 = null;
                address4 = new klass2("", this._offset, []);
                if (typeof type2 === "object") {
                  extend(address4, type2);
                }
                this._offset += 0;
              }
              if (address4) {
                elements0.push(address4);
                text0 += address4.textValue;
                labelled0.subscript = address4;
              } else {
                elements0 = null;
                this._offset = index1;
              }
            } else {
              elements0 = null;
              this._offset = index1;
            }
          } else {
            elements0 = null;
            this._offset = index1;
          }
        } else {
          elements0 = null;
          this._offset = index1;
        }
        if (elements0) {
          this._offset = index1;
          var klass3 = this.constructor.SyntaxNode;
          var type3 = null;
          address0 = new klass3(text0, this._offset, elements0, labelled0);
          if (typeof type3 === "object") {
            extend(address0, type3);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        return this._nodeCache["node_predicate"][index0] = address0;
      },
      __consume__expression: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["expression"] = this._nodeCache["expression"] || {};
        var cached = this._nodeCache["expression"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset;
        address0 = this.__consume__or_expression();
        if (address0) {
        } else {
          this._offset = index1;
          address0 = this.__consume__and_expression();
          if (address0) {
          } else {
            this._offset = index1;
            address0 = this.__consume__comparison();
            if (address0) {
            } else {
              this._offset = index1;
              address0 = this.__consume__atom();
              if (address0) {
              } else {
                this._offset = index1;
              }
            }
          }
        }
        return this._nodeCache["expression"][index0] = address0;
      },
      __consume__or_expression: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["or_expression"] = this._nodeCache["or_expression"] || {};
        var cached = this._nodeCache["or_expression"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset;
        var index2 = this._offset, elements0 = [], labelled0 = {}, text0 = "";
        var address1 = null;
        address1 = this.__consume__and_expression();
        if (address1) {
          elements0.push(address1);
          text0 += address1.textValue;
          labelled0.left = address1;
          var address2 = null;
          var slice0 = null;
          if (this._input.length > this._offset) {
            slice0 = this._input.substring(this._offset, this._offset + 2);
          } else {
            slice0 = null;
          }
          if (slice0 === "or") {
            var klass0 = this.constructor.SyntaxNode;
            var type0 = null;
            address2 = new klass0("or", this._offset, []);
            if (typeof type0 === "object") {
              extend(address2, type0);
            }
            this._offset += 2;
          } else {
            address2 = null;
            var slice1 = null;
            if (this._input.length > this._offset) {
              slice1 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice1 = null;
            }
            if (!this.error || this.error.offset <= this._offset) {
              this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"or\""};
            }
          }
          if (address2) {
            elements0.push(address2);
            text0 += address2.textValue;
            var address3 = null;
            address3 = this.__consume__or_expression();
            if (address3) {
              elements0.push(address3);
              text0 += address3.textValue;
              labelled0.right = address3;
            } else {
              elements0 = null;
              this._offset = index2;
            }
          } else {
            elements0 = null;
            this._offset = index2;
          }
        } else {
          elements0 = null;
          this._offset = index2;
        }
        if (elements0) {
          this._offset = index2;
          var klass1 = this.constructor.SyntaxNode;
          var type1 = find(this.constructor, "Or");
          address0 = new klass1(text0, this._offset, elements0, labelled0);
          if (typeof type1 === "object") {
            extend(address0, type1);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        if (address0) {
        } else {
          this._offset = index1;
          address0 = this.__consume__and_expression();
          if (address0) {
          } else {
            this._offset = index1;
          }
        }
        return this._nodeCache["or_expression"][index0] = address0;
      },
      __consume__and_expression: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["and_expression"] = this._nodeCache["and_expression"] || {};
        var cached = this._nodeCache["and_expression"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset;
        var index2 = this._offset, elements0 = [], labelled0 = {}, text0 = "";
        var address1 = null;
        address1 = this.__consume__comparison();
        if (address1) {
          elements0.push(address1);
          text0 += address1.textValue;
          labelled0.left = address1;
          var address2 = null;
          var slice0 = null;
          if (this._input.length > this._offset) {
            slice0 = this._input.substring(this._offset, this._offset + 3);
          } else {
            slice0 = null;
          }
          if (slice0 === "and") {
            var klass0 = this.constructor.SyntaxNode;
            var type0 = null;
            address2 = new klass0("and", this._offset, []);
            if (typeof type0 === "object") {
              extend(address2, type0);
            }
            this._offset += 3;
          } else {
            address2 = null;
            var slice1 = null;
            if (this._input.length > this._offset) {
              slice1 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice1 = null;
            }
            if (!this.error || this.error.offset <= this._offset) {
              this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"and\""};
            }
          }
          if (address2) {
            elements0.push(address2);
            text0 += address2.textValue;
            var address3 = null;
            address3 = this.__consume__and_expression();
            if (address3) {
              elements0.push(address3);
              text0 += address3.textValue;
              labelled0.right = address3;
            } else {
              elements0 = null;
              this._offset = index2;
            }
          } else {
            elements0 = null;
            this._offset = index2;
          }
        } else {
          elements0 = null;
          this._offset = index2;
        }
        if (elements0) {
          this._offset = index2;
          var klass1 = this.constructor.SyntaxNode;
          var type1 = find(this.constructor, "And");
          address0 = new klass1(text0, this._offset, elements0, labelled0);
          if (typeof type1 === "object") {
            extend(address0, type1);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        if (address0) {
        } else {
          this._offset = index1;
          address0 = this.__consume__comparison();
          if (address0) {
          } else {
            this._offset = index1;
          }
        }
        return this._nodeCache["and_expression"][index0] = address0;
      },
      __consume__comparison: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["comparison"] = this._nodeCache["comparison"] || {};
        var cached = this._nodeCache["comparison"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset;
        var index2 = this._offset, elements0 = [], labelled0 = {}, text0 = "";
        var address1 = null;
        address1 = this.__consume__atom();
        if (address1) {
          elements0.push(address1);
          text0 += address1.textValue;
          labelled0.left = address1;
          var address2 = null;
          address2 = this.__consume__comparator();
          if (address2) {
            elements0.push(address2);
            text0 += address2.textValue;
            labelled0.comparator = address2;
            var address3 = null;
            address3 = this.__consume__comparison();
            if (address3) {
              elements0.push(address3);
              text0 += address3.textValue;
              labelled0.right = address3;
            } else {
              elements0 = null;
              this._offset = index2;
            }
          } else {
            elements0 = null;
            this._offset = index2;
          }
        } else {
          elements0 = null;
          this._offset = index2;
        }
        if (elements0) {
          this._offset = index2;
          var klass0 = this.constructor.SyntaxNode;
          var type0 = find(this.constructor, "Comparison");
          address0 = new klass0(text0, this._offset, elements0, labelled0);
          if (typeof type0 === "object") {
            extend(address0, type0);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        if (address0) {
        } else {
          this._offset = index1;
          address0 = this.__consume__atom();
          if (address0) {
          } else {
            this._offset = index1;
          }
        }
        return this._nodeCache["comparison"][index0] = address0;
      },
      __consume__comparator: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["comparator"] = this._nodeCache["comparator"] || {};
        var cached = this._nodeCache["comparator"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset;
        var slice0 = null;
        if (this._input.length > this._offset) {
          slice0 = this._input.substring(this._offset, this._offset + 2);
        } else {
          slice0 = null;
        }
        if (slice0 === "!=") {
          var klass0 = this.constructor.SyntaxNode;
          var type0 = null;
          address0 = new klass0("!=", this._offset, []);
          if (typeof type0 === "object") {
            extend(address0, type0);
          }
          this._offset += 2;
        } else {
          address0 = null;
          var slice1 = null;
          if (this._input.length > this._offset) {
            slice1 = this._input.substring(this._offset, this._offset + 1);
          } else {
            slice1 = null;
          }
          if (!this.error || this.error.offset <= this._offset) {
            this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"!=\""};
          }
        }
        if (address0) {
        } else {
          this._offset = index1;
          var slice2 = null;
          if (this._input.length > this._offset) {
            slice2 = this._input.substring(this._offset, this._offset + 1);
          } else {
            slice2 = null;
          }
          if (slice2 === "=") {
            var klass1 = this.constructor.SyntaxNode;
            var type1 = null;
            address0 = new klass1("=", this._offset, []);
            if (typeof type1 === "object") {
              extend(address0, type1);
            }
            this._offset += 1;
          } else {
            address0 = null;
            var slice3 = null;
            if (this._input.length > this._offset) {
              slice3 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice3 = null;
            }
            if (!this.error || this.error.offset <= this._offset) {
              this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"=\""};
            }
          }
          if (address0) {
          } else {
            this._offset = index1;
          }
        }
        return this._nodeCache["comparator"][index0] = address0;
      },
      __consume__atom: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["atom"] = this._nodeCache["atom"] || {};
        var cached = this._nodeCache["atom"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset, elements0 = [], labelled0 = {}, text0 = "";
        var address1 = null;
        address1 = this.__consume__space();
        if (address1) {
          elements0.push(address1);
          text0 += address1.textValue;
          labelled0.space = address1;
          var address2 = null;
          var index2 = this._offset;
          var index3 = this._offset, elements1 = [], labelled1 = {}, text1 = "";
          var address3 = null;
          var slice0 = null;
          if (this._input.length > this._offset) {
            slice0 = this._input.substring(this._offset, this._offset + 1);
          } else {
            slice0 = null;
          }
          if (slice0 === "(") {
            var klass0 = this.constructor.SyntaxNode;
            var type0 = null;
            address3 = new klass0("(", this._offset, []);
            if (typeof type0 === "object") {
              extend(address3, type0);
            }
            this._offset += 1;
          } else {
            address3 = null;
            var slice1 = null;
            if (this._input.length > this._offset) {
              slice1 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice1 = null;
            }
            if (!this.error || this.error.offset <= this._offset) {
              this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"(\""};
            }
          }
          if (address3) {
            elements1.push(address3);
            text1 += address3.textValue;
            var address4 = null;
            address4 = this.__consume__expression();
            if (address4) {
              elements1.push(address4);
              text1 += address4.textValue;
              labelled1.in_parens = address4;
              var address5 = null;
              var slice2 = null;
              if (this._input.length > this._offset) {
                slice2 = this._input.substring(this._offset, this._offset + 1);
              } else {
                slice2 = null;
              }
              if (slice2 === ")") {
                var klass1 = this.constructor.SyntaxNode;
                var type1 = null;
                address5 = new klass1(")", this._offset, []);
                if (typeof type1 === "object") {
                  extend(address5, type1);
                }
                this._offset += 1;
              } else {
                address5 = null;
                var slice3 = null;
                if (this._input.length > this._offset) {
                  slice3 = this._input.substring(this._offset, this._offset + 1);
                } else {
                  slice3 = null;
                }
                if (!this.error || this.error.offset <= this._offset) {
                  this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\")\""};
                }
              }
              if (address5) {
                elements1.push(address5);
                text1 += address5.textValue;
              } else {
                elements1 = null;
                this._offset = index3;
              }
            } else {
              elements1 = null;
              this._offset = index3;
            }
          } else {
            elements1 = null;
            this._offset = index3;
          }
          if (elements1) {
            this._offset = index3;
            var klass2 = this.constructor.SyntaxNode;
            var type2 = null;
            address2 = new klass2(text1, this._offset, elements1, labelled1);
            if (typeof type2 === "object") {
              extend(address2, type2);
            }
            this._offset += text1.length;
          } else {
            address2 = null;
          }
          if (address2) {
          } else {
            this._offset = index2;
            address2 = this.__consume__value();
            if (address2) {
            } else {
              this._offset = index2;
            }
          }
          if (address2) {
            elements0.push(address2);
            text0 += address2.textValue;
            labelled0.expression = address2;
            var address6 = null;
            address6 = this.__consume__space();
            if (address6) {
              elements0.push(address6);
              text0 += address6.textValue;
              labelled0.space = address6;
            } else {
              elements0 = null;
              this._offset = index1;
            }
          } else {
            elements0 = null;
            this._offset = index1;
          }
        } else {
          elements0 = null;
          this._offset = index1;
        }
        if (elements0) {
          this._offset = index1;
          var klass3 = this.constructor.SyntaxNode;
          var type3 = find(this.constructor, "Atom");
          address0 = new klass3(text0, this._offset, elements0, labelled0);
          if (typeof type3 === "object") {
            extend(address0, type3);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        return this._nodeCache["atom"][index0] = address0;
      },
      __consume__value: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["value"] = this._nodeCache["value"] || {};
        var cached = this._nodeCache["value"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset;
        address0 = this.__consume__function_call();
        if (address0) {
        } else {
          this._offset = index1;
          address0 = this.__consume__string();
          if (address0) {
          } else {
            this._offset = index1;
            address0 = this.__consume__union_expression();
            if (address0) {
            } else {
              this._offset = index1;
            }
          }
        }
        return this._nodeCache["value"][index0] = address0;
      },
      __consume__function_call: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["function_call"] = this._nodeCache["function_call"] || {};
        var cached = this._nodeCache["function_call"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset, elements0 = [], labelled0 = {}, text0 = "";
        var address1 = null;
        address1 = this.__consume__function_name();
        if (address1) {
          elements0.push(address1);
          text0 += address1.textValue;
          labelled0.function_name = address1;
          var address2 = null;
          var slice0 = null;
          if (this._input.length > this._offset) {
            slice0 = this._input.substring(this._offset, this._offset + 1);
          } else {
            slice0 = null;
          }
          if (slice0 === "(") {
            var klass0 = this.constructor.SyntaxNode;
            var type0 = null;
            address2 = new klass0("(", this._offset, []);
            if (typeof type0 === "object") {
              extend(address2, type0);
            }
            this._offset += 1;
          } else {
            address2 = null;
            var slice1 = null;
            if (this._input.length > this._offset) {
              slice1 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice1 = null;
            }
            if (!this.error || this.error.offset <= this._offset) {
              this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"(\""};
            }
          }
          if (address2) {
            elements0.push(address2);
            text0 += address2.textValue;
            var address3 = null;
            address3 = this.__consume__function_args();
            if (address3) {
              elements0.push(address3);
              text0 += address3.textValue;
              labelled0.function_args = address3;
              var address4 = null;
              var slice2 = null;
              if (this._input.length > this._offset) {
                slice2 = this._input.substring(this._offset, this._offset + 1);
              } else {
                slice2 = null;
              }
              if (slice2 === ")") {
                var klass1 = this.constructor.SyntaxNode;
                var type1 = null;
                address4 = new klass1(")", this._offset, []);
                if (typeof type1 === "object") {
                  extend(address4, type1);
                }
                this._offset += 1;
              } else {
                address4 = null;
                var slice3 = null;
                if (this._input.length > this._offset) {
                  slice3 = this._input.substring(this._offset, this._offset + 1);
                } else {
                  slice3 = null;
                }
                if (!this.error || this.error.offset <= this._offset) {
                  this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\")\""};
                }
              }
              if (address4) {
                elements0.push(address4);
                text0 += address4.textValue;
              } else {
                elements0 = null;
                this._offset = index1;
              }
            } else {
              elements0 = null;
              this._offset = index1;
            }
          } else {
            elements0 = null;
            this._offset = index1;
          }
        } else {
          elements0 = null;
          this._offset = index1;
        }
        if (elements0) {
          this._offset = index1;
          var klass2 = this.constructor.SyntaxNode;
          var type2 = find(this.constructor, "FunctionCall");
          address0 = new klass2(text0, this._offset, elements0, labelled0);
          if (typeof type2 === "object") {
            extend(address0, type2);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        return this._nodeCache["function_call"][index0] = address0;
      },
      __consume__function_name: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["function_name"] = this._nodeCache["function_name"] || {};
        var cached = this._nodeCache["function_name"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var remaining0 = 1, index1 = this._offset, elements0 = [], text0 = "", address1 = true;
        while (address1) {
          var slice0 = null;
          if (this._input.length > this._offset) {
            slice0 = this._input.substring(this._offset, this._offset + 1);
          } else {
            slice0 = null;
          }
          if (slice0 && /^[a-z\-]/.test(slice0)) {
            var klass0 = this.constructor.SyntaxNode;
            var type0 = null;
            address1 = new klass0(slice0, this._offset, []);
            if (typeof type0 === "object") {
              extend(address1, type0);
            }
            this._offset += 1;
          } else {
            address1 = null;
            var slice1 = null;
            if (this._input.length > this._offset) {
              slice1 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice1 = null;
            }
            if (!this.error || this.error.offset <= this._offset) {
              this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "[a-z\\-]"};
            }
          }
          if (address1) {
            elements0.push(address1);
            text0 += address1.textValue;
            remaining0 -= 1;
          }
        }
        if (remaining0 <= 0) {
          this._offset = index1;
          var klass1 = this.constructor.SyntaxNode;
          var type1 = null;
          address0 = new klass1(text0, this._offset, elements0);
          if (typeof type1 === "object") {
            extend(address0, type1);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        return this._nodeCache["function_name"][index0] = address0;
      },
      __consume__function_args: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["function_args"] = this._nodeCache["function_args"] || {};
        var cached = this._nodeCache["function_args"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset;
        var index2 = this._offset, elements0 = [], labelled0 = {}, text0 = "";
        var address1 = null;
        address1 = this.__consume__expression();
        if (address1) {
          elements0.push(address1);
          text0 += address1.textValue;
          labelled0.first = address1;
          var address2 = null;
          var remaining0 = 0, index3 = this._offset, elements1 = [], text1 = "", address3 = true;
          while (address3) {
            var index4 = this._offset, elements2 = [], labelled1 = {}, text2 = "";
            var address4 = null;
            var slice0 = null;
            if (this._input.length > this._offset) {
              slice0 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice0 = null;
            }
            if (slice0 === ",") {
              var klass0 = this.constructor.SyntaxNode;
              var type0 = null;
              address4 = new klass0(",", this._offset, []);
              if (typeof type0 === "object") {
                extend(address4, type0);
              }
              this._offset += 1;
            } else {
              address4 = null;
              var slice1 = null;
              if (this._input.length > this._offset) {
                slice1 = this._input.substring(this._offset, this._offset + 1);
              } else {
                slice1 = null;
              }
              if (!this.error || this.error.offset <= this._offset) {
                this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\",\""};
              }
            }
            if (address4) {
              elements2.push(address4);
              text2 += address4.textValue;
              var address5 = null;
              address5 = this.__consume__expression();
              if (address5) {
                elements2.push(address5);
                text2 += address5.textValue;
                labelled1.expression = address5;
              } else {
                elements2 = null;
                this._offset = index4;
              }
            } else {
              elements2 = null;
              this._offset = index4;
            }
            if (elements2) {
              this._offset = index4;
              var klass1 = this.constructor.SyntaxNode;
              var type1 = null;
              address3 = new klass1(text2, this._offset, elements2, labelled1);
              if (typeof type1 === "object") {
                extend(address3, type1);
              }
              this._offset += text2.length;
            } else {
              address3 = null;
            }
            if (address3) {
              elements1.push(address3);
              text1 += address3.textValue;
              remaining0 -= 1;
            }
          }
          if (remaining0 <= 0) {
            this._offset = index3;
            var klass2 = this.constructor.SyntaxNode;
            var type2 = null;
            address2 = new klass2(text1, this._offset, elements1);
            if (typeof type2 === "object") {
              extend(address2, type2);
            }
            this._offset += text1.length;
          } else {
            address2 = null;
          }
          if (address2) {
            elements0.push(address2);
            text0 += address2.textValue;
            labelled0.rest = address2;
          } else {
            elements0 = null;
            this._offset = index2;
          }
        } else {
          elements0 = null;
          this._offset = index2;
        }
        if (elements0) {
          this._offset = index2;
          var klass3 = this.constructor.SyntaxNode;
          var type3 = null;
          address0 = new klass3(text0, this._offset, elements0, labelled0);
          if (typeof type3 === "object") {
            extend(address0, type3);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        if (address0) {
        } else {
          this._offset = index1;
          var klass4 = this.constructor.SyntaxNode;
          var type4 = null;
          address0 = new klass4("", this._offset, []);
          if (typeof type4 === "object") {
            extend(address0, type4);
          }
          this._offset += 0;
        }
        return this._nodeCache["function_args"][index0] = address0;
      },
      __consume__integer: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["integer"] = this._nodeCache["integer"] || {};
        var cached = this._nodeCache["integer"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset, elements0 = [], labelled0 = {}, text0 = "";
        var address1 = null;
        var slice0 = null;
        if (this._input.length > this._offset) {
          slice0 = this._input.substring(this._offset, this._offset + 1);
        } else {
          slice0 = null;
        }
        if (slice0 && /^[1-9]/.test(slice0)) {
          var klass0 = this.constructor.SyntaxNode;
          var type0 = null;
          address1 = new klass0(slice0, this._offset, []);
          if (typeof type0 === "object") {
            extend(address1, type0);
          }
          this._offset += 1;
        } else {
          address1 = null;
          var slice1 = null;
          if (this._input.length > this._offset) {
            slice1 = this._input.substring(this._offset, this._offset + 1);
          } else {
            slice1 = null;
          }
          if (!this.error || this.error.offset <= this._offset) {
            this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "[1-9]"};
          }
        }
        if (address1) {
          elements0.push(address1);
          text0 += address1.textValue;
          var address2 = null;
          var remaining0 = 0, index2 = this._offset, elements1 = [], text1 = "", address3 = true;
          while (address3) {
            var slice2 = null;
            if (this._input.length > this._offset) {
              slice2 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice2 = null;
            }
            if (slice2 && /^[0-9]/.test(slice2)) {
              var klass1 = this.constructor.SyntaxNode;
              var type1 = null;
              address3 = new klass1(slice2, this._offset, []);
              if (typeof type1 === "object") {
                extend(address3, type1);
              }
              this._offset += 1;
            } else {
              address3 = null;
              var slice3 = null;
              if (this._input.length > this._offset) {
                slice3 = this._input.substring(this._offset, this._offset + 1);
              } else {
                slice3 = null;
              }
              if (!this.error || this.error.offset <= this._offset) {
                this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "[0-9]"};
              }
            }
            if (address3) {
              elements1.push(address3);
              text1 += address3.textValue;
              remaining0 -= 1;
            }
          }
          if (remaining0 <= 0) {
            this._offset = index2;
            var klass2 = this.constructor.SyntaxNode;
            var type2 = null;
            address2 = new klass2(text1, this._offset, elements1);
            if (typeof type2 === "object") {
              extend(address2, type2);
            }
            this._offset += text1.length;
          } else {
            address2 = null;
          }
          if (address2) {
            elements0.push(address2);
            text0 += address2.textValue;
          } else {
            elements0 = null;
            this._offset = index1;
          }
        } else {
          elements0 = null;
          this._offset = index1;
        }
        if (elements0) {
          this._offset = index1;
          var klass3 = this.constructor.SyntaxNode;
          var type3 = find(this.constructor, "Integer");
          address0 = new klass3(text0, this._offset, elements0, labelled0);
          if (typeof type3 === "object") {
            extend(address0, type3);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        return this._nodeCache["integer"][index0] = address0;
      },
      __consume__string: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["string"] = this._nodeCache["string"] || {};
        var cached = this._nodeCache["string"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var index1 = this._offset;
        var index2 = this._offset, elements0 = [], labelled0 = {}, text0 = "";
        var address1 = null;
        var slice0 = null;
        if (this._input.length > this._offset) {
          slice0 = this._input.substring(this._offset, this._offset + 1);
        } else {
          slice0 = null;
        }
        if (slice0 === "'") {
          var klass0 = this.constructor.SyntaxNode;
          var type0 = null;
          address1 = new klass0("'", this._offset, []);
          if (typeof type0 === "object") {
            extend(address1, type0);
          }
          this._offset += 1;
        } else {
          address1 = null;
          var slice1 = null;
          if (this._input.length > this._offset) {
            slice1 = this._input.substring(this._offset, this._offset + 1);
          } else {
            slice1 = null;
          }
          if (!this.error || this.error.offset <= this._offset) {
            this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"'\""};
          }
        }
        if (address1) {
          elements0.push(address1);
          text0 += address1.textValue;
          var address2 = null;
          var remaining0 = 0, index3 = this._offset, elements1 = [], text1 = "", address3 = true;
          while (address3) {
            var index4 = this._offset;
            var index5 = this._offset, elements2 = [], labelled1 = {}, text2 = "";
            var address4 = null;
            var slice2 = null;
            if (this._input.length > this._offset) {
              slice2 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice2 = null;
            }
            if (slice2 === "\\") {
              var klass1 = this.constructor.SyntaxNode;
              var type1 = null;
              address4 = new klass1("\\", this._offset, []);
              if (typeof type1 === "object") {
                extend(address4, type1);
              }
              this._offset += 1;
            } else {
              address4 = null;
              var slice3 = null;
              if (this._input.length > this._offset) {
                slice3 = this._input.substring(this._offset, this._offset + 1);
              } else {
                slice3 = null;
              }
              if (!this.error || this.error.offset <= this._offset) {
                this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"\\\\\""};
              }
            }
            if (address4) {
              elements2.push(address4);
              text2 += address4.textValue;
              var address5 = null;
              var slice4 = null;
              if (this._input.length > this._offset) {
                slice4 = this._input.substring(this._offset, this._offset + 1);
              } else {
                slice4 = null;
              }
              var temp0 = slice4;
              if (temp0 === null) {
                address5 = null;
                var slice5 = null;
                if (this._input.length > this._offset) {
                  slice5 = this._input.substring(this._offset, this._offset + 1);
                } else {
                  slice5 = null;
                }
                if (!this.error || this.error.offset <= this._offset) {
                  this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "<any char>"};
                }
              } else {
                var klass2 = this.constructor.SyntaxNode;
                var type2 = null;
                address5 = new klass2(temp0, this._offset, []);
                if (typeof type2 === "object") {
                  extend(address5, type2);
                }
                this._offset += 1;
              }
              if (address5) {
                elements2.push(address5);
                text2 += address5.textValue;
              } else {
                elements2 = null;
                this._offset = index5;
              }
            } else {
              elements2 = null;
              this._offset = index5;
            }
            if (elements2) {
              this._offset = index5;
              var klass3 = this.constructor.SyntaxNode;
              var type3 = null;
              address3 = new klass3(text2, this._offset, elements2, labelled1);
              if (typeof type3 === "object") {
                extend(address3, type3);
              }
              this._offset += text2.length;
            } else {
              address3 = null;
            }
            if (address3) {
            } else {
              this._offset = index4;
              var slice6 = null;
              if (this._input.length > this._offset) {
                slice6 = this._input.substring(this._offset, this._offset + 1);
              } else {
                slice6 = null;
              }
              if (slice6 && /^[^']/.test(slice6)) {
                var klass4 = this.constructor.SyntaxNode;
                var type4 = null;
                address3 = new klass4(slice6, this._offset, []);
                if (typeof type4 === "object") {
                  extend(address3, type4);
                }
                this._offset += 1;
              } else {
                address3 = null;
                var slice7 = null;
                if (this._input.length > this._offset) {
                  slice7 = this._input.substring(this._offset, this._offset + 1);
                } else {
                  slice7 = null;
                }
                if (!this.error || this.error.offset <= this._offset) {
                  this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "[^']"};
                }
              }
              if (address3) {
              } else {
                this._offset = index4;
              }
            }
            if (address3) {
              elements1.push(address3);
              text1 += address3.textValue;
              remaining0 -= 1;
            }
          }
          if (remaining0 <= 0) {
            this._offset = index3;
            var klass5 = this.constructor.SyntaxNode;
            var type5 = null;
            address2 = new klass5(text1, this._offset, elements1);
            if (typeof type5 === "object") {
              extend(address2, type5);
            }
            this._offset += text1.length;
          } else {
            address2 = null;
          }
          if (address2) {
            elements0.push(address2);
            text0 += address2.textValue;
            var address6 = null;
            var slice8 = null;
            if (this._input.length > this._offset) {
              slice8 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice8 = null;
            }
            if (slice8 === "'") {
              var klass6 = this.constructor.SyntaxNode;
              var type6 = null;
              address6 = new klass6("'", this._offset, []);
              if (typeof type6 === "object") {
                extend(address6, type6);
              }
              this._offset += 1;
            } else {
              address6 = null;
              var slice9 = null;
              if (this._input.length > this._offset) {
                slice9 = this._input.substring(this._offset, this._offset + 1);
              } else {
                slice9 = null;
              }
              if (!this.error || this.error.offset <= this._offset) {
                this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"'\""};
              }
            }
            if (address6) {
              elements0.push(address6);
              text0 += address6.textValue;
            } else {
              elements0 = null;
              this._offset = index2;
            }
          } else {
            elements0 = null;
            this._offset = index2;
          }
        } else {
          elements0 = null;
          this._offset = index2;
        }
        if (elements0) {
          this._offset = index2;
          var klass7 = this.constructor.SyntaxNode;
          var type7 = null;
          address0 = new klass7(text0, this._offset, elements0, labelled0);
          if (typeof type7 === "object") {
            extend(address0, type7);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        if (address0) {
          var type8 = find(this.constructor, "String");
          if (typeof type8 === "object") {
            extend(address0, type8);
          }
        } else {
          this._offset = index1;
          var index6 = this._offset, elements3 = [], labelled2 = {}, text3 = "";
          var address7 = null;
          var slice10 = null;
          if (this._input.length > this._offset) {
            slice10 = this._input.substring(this._offset, this._offset + 1);
          } else {
            slice10 = null;
          }
          if (slice10 === "\"") {
            var klass8 = this.constructor.SyntaxNode;
            var type9 = null;
            address7 = new klass8("\"", this._offset, []);
            if (typeof type9 === "object") {
              extend(address7, type9);
            }
            this._offset += 1;
          } else {
            address7 = null;
            var slice11 = null;
            if (this._input.length > this._offset) {
              slice11 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice11 = null;
            }
            if (!this.error || this.error.offset <= this._offset) {
              this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"\\\"\""};
            }
          }
          if (address7) {
            elements3.push(address7);
            text3 += address7.textValue;
            var address8 = null;
            var remaining1 = 0, index7 = this._offset, elements4 = [], text4 = "", address9 = true;
            while (address9) {
              var index8 = this._offset;
              var index9 = this._offset, elements5 = [], labelled3 = {}, text5 = "";
              var address10 = null;
              var slice12 = null;
              if (this._input.length > this._offset) {
                slice12 = this._input.substring(this._offset, this._offset + 1);
              } else {
                slice12 = null;
              }
              if (slice12 === "\\") {
                var klass9 = this.constructor.SyntaxNode;
                var type10 = null;
                address10 = new klass9("\\", this._offset, []);
                if (typeof type10 === "object") {
                  extend(address10, type10);
                }
                this._offset += 1;
              } else {
                address10 = null;
                var slice13 = null;
                if (this._input.length > this._offset) {
                  slice13 = this._input.substring(this._offset, this._offset + 1);
                } else {
                  slice13 = null;
                }
                if (!this.error || this.error.offset <= this._offset) {
                  this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"\\\\\""};
                }
              }
              if (address10) {
                elements5.push(address10);
                text5 += address10.textValue;
                var address11 = null;
                var slice14 = null;
                if (this._input.length > this._offset) {
                  slice14 = this._input.substring(this._offset, this._offset + 1);
                } else {
                  slice14 = null;
                }
                var temp1 = slice14;
                if (temp1 === null) {
                  address11 = null;
                  var slice15 = null;
                  if (this._input.length > this._offset) {
                    slice15 = this._input.substring(this._offset, this._offset + 1);
                  } else {
                    slice15 = null;
                  }
                  if (!this.error || this.error.offset <= this._offset) {
                    this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "<any char>"};
                  }
                } else {
                  var klass10 = this.constructor.SyntaxNode;
                  var type11 = null;
                  address11 = new klass10(temp1, this._offset, []);
                  if (typeof type11 === "object") {
                    extend(address11, type11);
                  }
                  this._offset += 1;
                }
                if (address11) {
                  elements5.push(address11);
                  text5 += address11.textValue;
                } else {
                  elements5 = null;
                  this._offset = index9;
                }
              } else {
                elements5 = null;
                this._offset = index9;
              }
              if (elements5) {
                this._offset = index9;
                var klass11 = this.constructor.SyntaxNode;
                var type12 = null;
                address9 = new klass11(text5, this._offset, elements5, labelled3);
                if (typeof type12 === "object") {
                  extend(address9, type12);
                }
                this._offset += text5.length;
              } else {
                address9 = null;
              }
              if (address9) {
              } else {
                this._offset = index8;
                var slice16 = null;
                if (this._input.length > this._offset) {
                  slice16 = this._input.substring(this._offset, this._offset + 1);
                } else {
                  slice16 = null;
                }
                if (slice16 && /^[^"]/.test(slice16)) {
                  var klass12 = this.constructor.SyntaxNode;
                  var type13 = null;
                  address9 = new klass12(slice16, this._offset, []);
                  if (typeof type13 === "object") {
                    extend(address9, type13);
                  }
                  this._offset += 1;
                } else {
                  address9 = null;
                  var slice17 = null;
                  if (this._input.length > this._offset) {
                    slice17 = this._input.substring(this._offset, this._offset + 1);
                  } else {
                    slice17 = null;
                  }
                  if (!this.error || this.error.offset <= this._offset) {
                    this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "[^\"]"};
                  }
                }
                if (address9) {
                } else {
                  this._offset = index8;
                }
              }
              if (address9) {
                elements4.push(address9);
                text4 += address9.textValue;
                remaining1 -= 1;
              }
            }
            if (remaining1 <= 0) {
              this._offset = index7;
              var klass13 = this.constructor.SyntaxNode;
              var type14 = null;
              address8 = new klass13(text4, this._offset, elements4);
              if (typeof type14 === "object") {
                extend(address8, type14);
              }
              this._offset += text4.length;
            } else {
              address8 = null;
            }
            if (address8) {
              elements3.push(address8);
              text3 += address8.textValue;
              var address12 = null;
              var slice18 = null;
              if (this._input.length > this._offset) {
                slice18 = this._input.substring(this._offset, this._offset + 1);
              } else {
                slice18 = null;
              }
              if (slice18 === "\"") {
                var klass14 = this.constructor.SyntaxNode;
                var type15 = null;
                address12 = new klass14("\"", this._offset, []);
                if (typeof type15 === "object") {
                  extend(address12, type15);
                }
                this._offset += 1;
              } else {
                address12 = null;
                var slice19 = null;
                if (this._input.length > this._offset) {
                  slice19 = this._input.substring(this._offset, this._offset + 1);
                } else {
                  slice19 = null;
                }
                if (!this.error || this.error.offset <= this._offset) {
                  this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\"\\\"\""};
                }
              }
              if (address12) {
                elements3.push(address12);
                text3 += address12.textValue;
              } else {
                elements3 = null;
                this._offset = index6;
              }
            } else {
              elements3 = null;
              this._offset = index6;
            }
          } else {
            elements3 = null;
            this._offset = index6;
          }
          if (elements3) {
            this._offset = index6;
            var klass15 = this.constructor.SyntaxNode;
            var type16 = null;
            address0 = new klass15(text3, this._offset, elements3, labelled2);
            if (typeof type16 === "object") {
              extend(address0, type16);
            }
            this._offset += text3.length;
          } else {
            address0 = null;
          }
          if (address0) {
            var type17 = find(this.constructor, "String");
            if (typeof type17 === "object") {
              extend(address0, type17);
            }
          } else {
            this._offset = index1;
          }
        }
        return this._nodeCache["string"][index0] = address0;
      },
      __consume__space: function() {
        var address0 = null, index0 = this._offset;
        this._nodeCache["space"] = this._nodeCache["space"] || {};
        var cached = this._nodeCache["space"][index0];
        if (cached) {
          this._offset += cached.textValue.length;
          return cached;
        }
        var remaining0 = 0, index1 = this._offset, elements0 = [], text0 = "", address1 = true;
        while (address1) {
          var slice0 = null;
          if (this._input.length > this._offset) {
            slice0 = this._input.substring(this._offset, this._offset + 1);
          } else {
            slice0 = null;
          }
          if (slice0 === " ") {
            var klass0 = this.constructor.SyntaxNode;
            var type0 = null;
            address1 = new klass0(" ", this._offset, []);
            if (typeof type0 === "object") {
              extend(address1, type0);
            }
            this._offset += 1;
          } else {
            address1 = null;
            var slice1 = null;
            if (this._input.length > this._offset) {
              slice1 = this._input.substring(this._offset, this._offset + 1);
            } else {
              slice1 = null;
            }
            if (!this.error || this.error.offset <= this._offset) {
              this.error = this.constructor.lastError = {input: this._input, offset: this._offset, expected: "\" \""};
            }
          }
          if (address1) {
            elements0.push(address1);
            text0 += address1.textValue;
            remaining0 -= 1;
          }
        }
        if (remaining0 <= 0) {
          this._offset = index1;
          var klass1 = this.constructor.SyntaxNode;
          var type1 = null;
          address0 = new klass1(text0, this._offset, elements0);
          if (typeof type1 === "object") {
            extend(address0, type1);
          }
          this._offset += text0.length;
        } else {
          address0 = null;
        }
        return this._nodeCache["space"][index0] = address0;
      }
    };
  
    var Parser = function(input) {
      this._input = input;
      this._offset = 0;
      this._nodeCache = {};
    };
  
    Parser.prototype.parse = function() {
      var result = this.__consume__union_expression();
      if (result && this._offset === this._input.length) {
        return result;
      }
      if (!(this.error)) {
        this.error = {input: this._input, offset: this._offset, expected: "<EOF>"};
      }
      var message = formatError(this.error);
      var error = new Error(message);
      throw error;
    };
  
    Parser.parse = function(input) {
      var parser = new Parser(input);
      return parser.parse();
    };
  
    extend(Parser.prototype, Grammar);
  
    var SyntaxNode = function(textValue, offset, elements, properties) {
      this.textValue = textValue;
      this.offset    = offset;
      this.elements  = elements || [];
      if (!properties) return;
      for (var key in properties) this[key] = properties[key];
    };
  
    SyntaxNode.prototype.forEach = function(block, context) {
      for (var i = 0, n = this.elements.length; i < n; i++) {
        block.call(context, this.elements[i], i);
      }
    };
  
    Parser.SyntaxNode = SyntaxNode;
  
    if (typeof require === "function" && typeof exports === "object") {
      exports.Grammar = Grammar;
      exports.Parser  = Parser;
      exports.parse   = Parser.parse;
  
      if (typeof Pathology !== "undefined") {
        Pathology.XPath = Grammar;
        Pathology.XPathParser = Parser;
        Pathology.XPathParser.formatError = formatError;
      }
    } else {
      var namespace = this;
      namespace = namespace.Pathology = namespace.Pathology || {};
      Pathology.XPath = Grammar;
      Pathology.XPathParser = Parser;
      Pathology.XPathParser.formatError = formatError;
    }
  })();
  
  
  Pathology.XPathParser.And = {
    evaluate: function(context, root) {
      return Pathology.atomize(this.left, context, root) &&
             Pathology.atomize(this.right, context, root);
    }
  };
  
  
  Pathology.XPathParser.Atom = {
    evaluate: function(context, root) {
      var expression = this.expression.in_parens || this.expression;
      return expression.evaluate(context, root);
    }
  };
  
  
  Pathology.Axis = function(name) {
    this.name = name;
  };
  
  Pathology.Axis.prototype.walk = function(context, block, scope) {
    var children   = context.childNodes,
        attributes = Pathology.array(context.attributes),
        sibling;
  
    if (context.checked)
      attributes.push({ nodeName:   'checked',
                        nodeValue:  true,
                        nodeType:   XPathResult.STRING_TYPE
                     });
  
    if (context.selected)
      attributes.push({ nodeName:   'selected',
                        nodeValue:  true,
                        nodeType:   XPathResult.STRING_TYPE
                     });
  
    switch (this.name) {
      case 'attribute':
        for (var i = 0, n = attributes.length; i < n; i++) {
          block.call(scope, attributes[i]);
        }
        break;
  
      case 'child':
        for (var i = 0, n = children.length; i < n; i++) {
          block.call(scope, children[i]);
        }
        break;
  
      case 'descendant-or-self':
        block.call(scope, context);
        for (var i = 0, n = children.length; i < n; i++) {
          this.walk(children[i], block, scope);
        }
        break;
  
      case 'following-sibling':
        sibling = context.nextSibling;
        while (sibling) {
          block.call(scope, sibling);
          sibling = sibling.nextSibling;
        }
        break;
  
      case 'parent':
        block.call(scope, context.parentNode);
        break;
  
      case 'self':
        block.call(scope, context);
        break;
    }
  };
  
  Pathology.Axis.SHORTHANDS = {
    '@' : 'attribute',
    '..': 'parent',
    '.' : 'self',
    '/' : 'descendant-or-self',
    ''  : 'child'
  };
  
  Pathology.Axis.fromAST = function(node) {
    var name = node.axis_name
             ? node.axis_name.textValue
             : node.textValue;
  
    return new this(this.SHORTHANDS[name] || name);
  };
  
  
  Pathology.XPathParser.Comparison = {
    evaluate: function(context, root) {
      // TODO make this symmetric
      var comparator = this.comparator.textValue,
          left       = this.left.evaluate(context, root),
          right      = Pathology.atomize(this.right, context, root),
          viable     = false,
          array      = (right instanceof Array),
          node;
  
      if (left._nodes) {
        for (var i = 0, n = left._nodes.length; i < n; i++) {
          node = left._nodes[i];
          if (comparator === '=') {
            viable = viable || (array ? Pathology.indexOf(right, node.nodeValue) >= 0 : (node.nodeValue === right || node.innerHTML === right));
          } else if (comparator === '!=') {
            viable = viable || (array ? Pathology.indexOf(right, node.nodeValue) < 0 : (node.nodeValue !== right && node.innerHTML !== right));
          }
        }
        return viable;
  
      } else {
        switch (comparator) {
          case '=':   return array ? Pathology.indexOf(right, left.nodeValue) >= 0 : left == right;
          case '!=':  return array ? Pathology.indexOf(right, node.nodeValue) <  0 : left != right;
        }
      }
    }
  };
  
  
  Pathology.XPathParser.FunctionCall = {
    getArguments: function(context, root) {
      var args = [],
          rest = this.function_args.rest;
  
      if (this.function_args.first && this.function_args.first.evaluate) {
        args.push(this.function_args.first.evaluate(context, root));
      }
      if (rest) {
        for (var i = 0, n = rest.elements.length; i < n; i++)
          args.push(rest.elements[i].expression.evaluate(context, root));
      }
      return args;
    },
  
    evaluate: function(context, root) {
      var args = this.getArguments(context, root),
          proc = this.REGISTER[this.function_name.textValue];
  
      return proc.apply(context, args);
    },
  
    REGISTER: {
      'concat': function() {
        var string = '';
        for (var i = 0, n = arguments.length; i < n; i++)
          string += arguments[i].makeString ? arguments[i].makeString() : arguments[i];
        return string;
      },
  
      'contains': function(haystack, needle) {
        if (!haystack) return false;
        if (haystack.makeString) haystack = haystack.makeString();
  
        return haystack.toString().indexOf(needle) >= 0;
      },
  
      'normalize-space': function(string) {
        if (string.makeString) string = string.makeString();
  
        return string.toString().replace(/^\s*/g, '')
                                .replace(/\s*$/g, '')
                                .replace(/\s+/g, ' ');
      },
  
      'name': function() {
        return this.nodeName.toLowerCase();
      },
  
      'not': function(value) {
        if (value && value.atomize) value = value.atomize();
        if (typeof value === 'string') return false;
        return !value;
      },
  
      'string': function(value) {
        return value.atomize().innerText;
      },
  
      'text': function() {
        return document.evaluate('/text()', this, null, XPathResult.ANY_TYPE, null);
      }
    }
  };
  
  
  Pathology.XPathParser.LocationPath = {
    eachStep: function(block, scope) {
      var list = [this.head].concat(this.rest.elements);
      for (var i = 0, n = list.length; i < n; i++)
        block.call(scope, list[i]);
    },
  
    evaluate: function(context, root, resultType, result) {
      result = result || new Pathology.XPathResult(XPathResult.ANY_TYPE);
      resultType = resultType || XPathResult.ANY_TYPE;
  
      var intermediate = new Pathology.XPathResult(resultType),
          startNode    = this.head.isRelative() ? context : root,
          steps        = [this.head].concat(this.rest.elements),
          step,
          nextStage,
          i, j, n, m;
  
      intermediate.push(startNode);
  
      for (i = 0, n = steps.length; i < n; i++) {
        step = steps[i];
        nextStage = new Pathology.XPathResult(resultType);
        for (j = 0, m = intermediate._nodes.length; j < m; j++) {
          step.evaluate(intermediate._nodes[j], root, resultType, nextStage);
        }
        intermediate = nextStage;
      }
  
      for (i = 0, n = intermediate._nodes.length; i < n; i++)
        result.push(intermediate._nodes[i]);
  
      return result;
    }
  };
  
  
  Pathology.XPathParser.LocationStep = {
    isRelative: function() {
      return this.elements[0].textValue !== '/';
    },
  
    evaluate: function(context, root, resultType, result) {
      var axis   = this.selector.axis,
          test   = this.selector.test,
          levels = [[]];
  
      Pathology.Axis.fromAST(axis).walk(context, function(node) {
        if (!test || !test.evaluate) return result.push(node);
        test.evaluate(node, this.predicates, root, resultType, levels, result);
      }, this);
    }
  };
  
  
  Pathology.XPathParser.NodeTest = {
    evaluate: function(context, predicates, root, resultType, levels, result) {
      var name    = this.elements[0].condition_name,
          tagName = tagName = this.elements[0].textValue.toLowerCase();
  
      var first = {
        expression: {
          evaluate: function() {
            if (name && name.textValue === 'node') {
              return true;
            } else if (name && name.textValue === 'text') {
              if (context.nodeType !== XPathResult.BOOLEAN_TYPE) return false;
            } else {
              if (tagName === '*') {
                if (context.nodeType !== 1) return false;
              } else {
                if (!context.nodeName) return false;
                if (context.nodeName.toLowerCase() !== tagName) return false;
              }
            }
            return true;
          }
        },
        subscript: this.subscript
      };
  
      predicates = [first].concat(predicates.elements);
      var accepted, predicate;
  
      for (var i = 0, n = predicates.length; i < n; i++) {
        levels[i] = levels[i] || [];
        predicate = predicates[i];
  
        accepted = Pathology.atomize(predicate.expression, context, root);
        if (typeof accepted === 'string') accepted = true;
        if (!accepted) return;
  
        levels[i].push(context);
  
        if (predicate.subscript.integer) {
          if (predicate.subscript.integer.evaluate() !== levels[i].length) return;
        }
      }
  
      result.push(context);
    }
  };
  
  
  Pathology.XPathParser.Or = {
    evaluate: function(context, root) {
      return Pathology.atomize(this.left, context, root) ||
             Pathology.atomize(this.right, context, root);
    }
  };
  
  
  Pathology.XPathParser.Integer = {
    evaluate: function(context, root) {
      return parseInt(this.textValue, 10);
    }
  };
  
  
  Pathology.XPathParser.String = {
    evaluate: function(context, root) {
      return eval(this.textValue);
    }
  };
  
  
  Pathology.XPathParser.Union = {
    evaluate: function(context, root, resultType, result) {
      result = result || new Pathology.XPathResult(XPathResult.ANY_TYPE);
      resultType = resultType || XPathResult.ANY_TYPE;
  
      this.head.evaluate(context, root, resultType, result);
  
      var sections = this.rest.elements;
      for (var i = 0, n = sections.length; i < n; i++)
        sections[i].location_path.evaluate(context, root, resultType, result);
  
      return result;
    }
  };
  
  