text: // **N3Parser** parses N3 documents.
  var N3Lexer = require('./N3Lexer');
  
  var RDF_PREFIX = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
      RDF_NIL    = RDF_PREFIX + 'nil',
      RDF_FIRST  = RDF_PREFIX + 'first',
      RDF_REST   = RDF_PREFIX + 'rest';
  
  var QUANTIFIERS_GRAPH = 'urn:n3:quantifiers';
  
  var absoluteIRI = /^[a-z][a-z0-9+.-]*:/i,
      schemeAuthority = /^(?:([a-z][a-z0-9+.-]*:))?(?:\/\/[^\/]*)?/i,
      dotSegments = /(?:^|\/)\.\.?(?:$|[\/#?])/;
  
  // The next ID for new blank nodes
  var blankNodePrefix = 0, blankNodeCount = 0;
  
  // ## Constructor
  function N3Parser(options) {
    if (!(this instanceof N3Parser))
      return new N3Parser(options);
    this._contextStack = [];
    this._graph = null;
  
    // Set the document IRI
    options = options || {};
    this._setBase(options.documentIRI);
  
    // Set supported features depending on the format
    var format = (typeof options.format === 'string') ?
                 options.format.match(/\w*$/)[0].toLowerCase() : '',
        isTurtle = format === 'turtle', isTriG = format === 'trig',
        isNTriples = /triple/.test(format), isNQuads = /quad/.test(format),
        isN3 = this._n3Mode = /n3/.test(format),
        isLineMode = isNTriples || isNQuads;
    if (!(this._supportsNamedGraphs = !(isTurtle || isN3)))
      this._readPredicateOrNamedGraph = this._readPredicate;
    this._supportsQuads = !(isTurtle || isTriG || isNTriples || isN3);
    // Disable relative IRIs in N-Triples or N-Quads mode
    if (isLineMode) {
      this._base = '';
      this._resolveIRI = function (token) {
        this._error('Disallowed relative IRI', token);
        return this._callback = noop, this._subject = null;
      };
    }
    this._blankNodePrefix = typeof options.blankNodePrefix !== 'string' ? '' :
                              '_:' + options.blankNodePrefix.replace(/^_:/, '');
    this._lexer = options.lexer || new N3Lexer({ lineMode: isLineMode, n3: isN3 });
    // Disable explicit quantifiers by default
    this._explicitQuantifiers = !!options.explicitQuantifiers;
  }
  
  // ## Private class methods
  
  // ### `_resetBlankNodeIds` restarts blank node identification
  N3Parser._resetBlankNodeIds = function () {
    blankNodePrefix = blankNodeCount = 0;
  };
  
  N3Parser.prototype = {
    // ## Private methods
  
    // ### `_setBase` sets the base IRI to resolve relative IRIs
    _setBase: function (baseIRI) {
      if (!baseIRI)
        this._base = null;
      else {
        // Remove fragment if present
        var fragmentPos = baseIRI.indexOf('#');
        if (fragmentPos >= 0)
          baseIRI = baseIRI.substr(0, fragmentPos);
        // Set base IRI and its components
        this._base = baseIRI;
        this._basePath   = baseIRI.indexOf('/') < 0 ? baseIRI :
                           baseIRI.replace(/[^\/?]*(?:\?.*)?$/, '');
        baseIRI = baseIRI.match(schemeAuthority);
        this._baseRoot   = baseIRI[0];
        this._baseScheme = baseIRI[1];
      }
    },
  
    // ### `_saveContext` stores the current parsing context
    // when entering a new scope (list, blank node, formula)
    _saveContext: function (type, graph, subject, predicate, object) {
      var n3Mode = this._n3Mode;
      this._contextStack.push({
        subject: subject, predicate: predicate, object: object,
        graph: graph, type: type,
        inverse: n3Mode ? this._inversePredicate : false,
        blankPrefix: n3Mode ? this._prefixes._ : '',
        quantified: n3Mode ? this._quantified : null,
      });
      // The settings below only apply to N3 streams
      if (n3Mode) {
        // Every new scope resets the predicate direction
        this._inversePredicate = false;
        // In N3, blank nodes are scoped to a formula
        // (using a dot as separator, as a blank node label cannot start with it)
        this._prefixes._ = this._graph + '.';
        // Quantifiers are scoped to a formula
        this._quantified = Object.create(this._quantified);
      }
    },
  
    // ### `_restoreContext` restores the parent context
    // when leaving a scope (list, blank node, formula)
    _restoreContext: function () {
      var context = this._contextStack.pop(), n3Mode = this._n3Mode;
      this._subject   = context.subject;
      this._predicate = context.predicate;
      this._object    = context.object;
      this._graph     = context.graph;
      // The settings below only apply to N3 streams
      if (n3Mode) {
        this._inversePredicate = context.inverse;
        this._prefixes._ = context.blankPrefix;
        this._quantified = context.quantified;
      }
    },
  
    // ### `_readInTopContext` reads a token when in the top context
    _readInTopContext: function (token) {
      switch (token.type) {
      // If an EOF token arrives in the top context, signal that we're done
      case 'eof':
        if (this._graph !== null)
          return this._error('Unclosed graph', token);
        delete this._prefixes._;
        return this._callback(null, null, this._prefixes);
      // It could be a prefix declaration
      case 'PREFIX':
        this._sparqlStyle = true;
      case '@prefix':
        return this._readPrefix;
      // It could be a base declaration
      case 'BASE':
        this._sparqlStyle = true;
      case '@base':
        return this._readBaseIRI;
      // It could be a graph
      case '{':
        if (this._supportsNamedGraphs) {
          this._graph = '';
          this._subject = null;
          return this._readSubject;
        }
      case 'GRAPH':
        if (this._supportsNamedGraphs)
          return this._readNamedGraphLabel;
      // Otherwise, the next token must be a subject
      default:
        return this._readSubject(token);
      }
    },
  
    // ### `_readEntity` reads an IRI, prefixed name, blank node, or variable
    _readEntity: function (token, quantifier) {
      var value;
      switch (token.type) {
      // Read a relative or absolute IRI
      case 'IRI':
      case 'typeIRI':
        value = (this._base === null || absoluteIRI.test(token.value)) ?
                token.value : this._resolveIRI(token);
        break;
      // Read a blank node or prefixed name
      case 'type':
      case 'blank':
      case 'prefixed':
        var prefix = this._prefixes[token.prefix];
        if (prefix === undefined)
          return this._error('Undefined prefix "' + token.prefix + ':"', token);
        value = prefix + token.value;
        break;
      // Read a variable
      case 'var':
        return token.value;
      // Everything else is not an entity
      default:
        return this._error('Expected entity but got ' + token.type, token);
      }
      // In N3 mode, replace the entity if it is quantified
      if (!quantifier && this._n3Mode && (value in this._quantified))
        value = this._quantified[value];
      return value;
    },
  
    // ### `_readSubject` reads a triple's subject
    _readSubject: function (token) {
      this._predicate = null;
      switch (token.type) {
      case '[':
        // Start a new triple with a new blank node as subject
        this._saveContext('blank', this._graph,
                          this._subject = '_:b' + blankNodeCount++, null, null);
        return this._readBlankNodeHead;
      case '(':
        // Start a new list
        this._saveContext('list', this._graph, RDF_NIL, null, null);
        this._subject = null;
        return this._readListItem;
      case '{':
        // Start a new formula
        if (!this._n3Mode)
          return this._error('Unexpected graph', token);
        this._saveContext('formula', this._graph,
                          this._graph = '_:b' + blankNodeCount++, null, null);
        return this._readSubject;
      case '}':
         // No subject; the graph in which we are reading is closed instead
        return this._readPunctuation(token);
      case '@forSome':
        this._subject = null;
        this._predicate = 'http://www.w3.org/2000/10/swap/reify#forSome';
        this._quantifiedPrefix = '_:b';
        return this._readQuantifierList;
      case '@forAll':
        this._subject = null;
        this._predicate = 'http://www.w3.org/2000/10/swap/reify#forAll';
        this._quantifiedPrefix = '?b-';
        return this._readQuantifierList;
      default:
        // Read the subject entity
        if ((this._subject = this._readEntity(token)) === undefined)
          return;
        // In N3 mode, the subject might be a path
        if (this._n3Mode)
          return this._getPathReader(this._readPredicateOrNamedGraph);
      }
  
      // The next token must be a predicate,
      // or, if the subject was actually a graph IRI, a named graph
      return this._readPredicateOrNamedGraph;
    },
  
    // ### `_readPredicate` reads a triple's predicate
    _readPredicate: function (token) {
      var type = token.type;
      switch (type) {
      case 'inverse':
        this._inversePredicate = true;
      case 'abbreviation':
        this._predicate = token.value;
        break;
      case '.':
      case ']':
      case '}':
        // Expected predicate didn't come, must have been trailing semicolon
        if (this._predicate === null)
          return this._error('Unexpected ' + type, token);
        this._subject = null;
        return type === ']' ? this._readBlankNodeTail(token) : this._readPunctuation(token);
      case ';':
        // Extra semicolons can be safely ignored
        return this._readPredicate;
      case 'blank':
        if (!this._n3Mode)
          return this._error('Disallowed blank node as predicate', token);
      default:
        if ((this._predicate = this._readEntity(token)) === undefined)
          return;
      }
      // The next token must be an object
      return this._readObject;
    },
  
    // ### `_readObject` reads a triple's object
    _readObject: function (token) {
      switch (token.type) {
      case 'literal':
        this._object = token.value;
        return this._readDataTypeOrLang;
      case '[':
        // Start a new triple with a new blank node as subject
        this._saveContext('blank', this._graph, this._subject, this._predicate,
                          this._subject = '_:b' + blankNodeCount++);
        return this._readBlankNodeHead;
      case '(':
        // Start a new list
        this._saveContext('list', this._graph, this._subject, this._predicate, RDF_NIL);
        this._subject = null;
        return this._readListItem;
      case '{':
        // Start a new formula
        if (!this._n3Mode)
          return this._error('Unexpected graph', token);
        this._saveContext('formula', this._graph, this._subject, this._predicate,
                          this._graph = '_:b' + blankNodeCount++);
        return this._readSubject;
      default:
        // Read the object entity
        if ((this._object = this._readEntity(token)) === undefined)
          return;
        // In N3 mode, the object might be a path
        if (this._n3Mode)
          return this._getPathReader(this._getContextEndReader());
      }
      return this._getContextEndReader();
    },
  
    // ### `_readPredicateOrNamedGraph` reads a triple's predicate, or a named graph
    _readPredicateOrNamedGraph: function (token) {
      return token.type === '{' ? this._readGraph(token) : this._readPredicate(token);
    },
  
    // ### `_readGraph` reads a graph
    _readGraph: function (token) {
      if (token.type !== '{')
        return this._error('Expected graph but got ' + token.type, token);
      // The "subject" we read is actually the GRAPH's label
      this._graph = this._subject, this._subject = null;
      return this._readSubject;
    },
  
    // ### `_readBlankNodeHead` reads the head of a blank node
    _readBlankNodeHead: function (token) {
      if (token.type === ']') {
        this._subject = null;
        return this._readBlankNodeTail(token);
      }
      else {
        this._predicate = null;
        return this._readPredicate(token);
      }
    },
  
    // ### `_readBlankNodeTail` reads the end of a blank node
    _readBlankNodeTail: function (token) {
      if (token.type !== ']')
        return this._readBlankNodePunctuation(token);
  
      // Store blank node triple
      if (this._subject !== null)
        this._triple(this._subject, this._predicate, this._object, this._graph);
  
      // Restore the parent context containing this blank node
      var empty = this._predicate === null;
      this._restoreContext();
      // If the blank node was the subject, continue reading the predicate
      if (this._object === null)
        // If the blank node was empty, it could be a named graph label
        return empty ? this._readPredicateOrNamedGraph : this._readPredicateAfterBlank;
      // If the blank node was the object, restore previous context and read punctuation
      else
        return this._getContextEndReader();
    },
  
    // ### `_readPredicateAfterBlank` reads a predicate after an anonymous blank node
    _readPredicateAfterBlank: function (token) {
      // If a dot follows a blank node in top context, there is no predicate
      if (token.type === '.' && !this._contextStack.length) {
        this._subject = null; // cancel the current triple
        return this._readPunctuation(token);
      }
      return this._readPredicate(token);
    },
  
    // ### `_readListItem` reads items from a list
    _readListItem: function (token) {
      var item = null,                      // The item of the list
          list = null,                      // The list itself
          prevList = this._subject,         // The previous list that contains this list
          stack = this._contextStack,       // The stack of parent contexts
          parent = stack[stack.length - 1], // The parent containing the current list
          next = this._readListItem,        // The next function to execute
          itemComplete = true;              // Whether the item has been read fully
  
      switch (token.type) {
      case '[':
        // Stack the current list triple and start a new triple with a blank node as subject
        this._saveContext('blank', this._graph, list = '_:b' + blankNodeCount++,
                          RDF_FIRST, this._subject = item = '_:b' + blankNodeCount++);
        next = this._readBlankNodeHead;
        break;
      case '(':
        // Stack the current list triple and start a new list
        this._saveContext('list', this._graph, list = '_:b' + blankNodeCount++,
                          RDF_FIRST, RDF_NIL);
        this._subject = null;
        break;
      case ')':
        // Closing the list; restore the parent context
        this._restoreContext();
        // If this list is contained within a parent list, return the membership triple here.
        // This will be `<parent list element> rdf:first <this list>.`.
        if (stack.length !== 0 && stack[stack.length - 1].type === 'list')
          this._triple(this._subject, this._predicate, this._object, this._graph);
        // Was this list the parent's subject?
        if (this._predicate === null) {
          // The next token is the predicate
          next = this._readPredicate;
          // No list tail if this was an empty list
          if (this._subject === RDF_NIL)
            return next;
        }
        // The list was in the parent context's object
        else {
          next = this._getContextEndReader();
          // No list tail if this was an empty list
          if (this._object === RDF_NIL)
            return next;
        }
        // Close the list by making the head nil
        list = RDF_NIL;
        break;
      case 'literal':
        item = token.value;
        itemComplete = false; // Can still have a datatype or language
        next = this._readListItemDataTypeOrLang;
        break;
      default:
        if ((item = this._readEntity(token)) === undefined)
          return;
      }
  
       // Create a new blank node if no item head was assigned yet
      if (list === null)
        this._subject = list = '_:b' + blankNodeCount++;
  
      // Is this the first element of the list?
      if (prevList === null) {
        // This list is either the subject or the object of its parent
        if (parent.predicate === null)
          parent.subject = list;
        else
          parent.object = list;
      }
      else {
        // Continue the previous list with the current list
        this._triple(prevList, RDF_REST, list, this._graph);
      }
      // Add the item's value
      if (item !== null) {
        // In N3 mode, the item might be a path
        if (this._n3Mode && (token.type === 'IRI' || token.type === 'prefixed')) {
          // Create a new context to add the item's path
          this._saveContext('item', this._graph, list, RDF_FIRST, item);
          this._subject = item, this._predicate = null;
          // _readPath will restore the context and output the item
          return this._getPathReader(this._readListItem);
        }
        // Output the item if it is complete
        if (itemComplete)
          this._triple(list, RDF_FIRST, item, this._graph);
        // Otherwise, save it for completion
        else
          this._object = item;
      }
      return next;
    },
  
    // ### `_readDataTypeOrLang` reads an _optional_ data type or language
    _readDataTypeOrLang: function (token) {
      return this._completeLiteral(token, false);
    },
  
    // ### `_readListItemDataTypeOrLang` reads an _optional_ data type or language in a list
    _readListItemDataTypeOrLang: function (token) {
      return this._completeLiteral(token, true);
    },
  
    // ### `_completeLiteral` completes the object with a data type or language
    _completeLiteral: function (token, listItem) {
      var suffix = false;
      switch (token.type) {
      // Add a "^^type" suffix for types (IRIs and blank nodes)
      case 'type':
      case 'typeIRI':
        suffix = true;
        this._object += '^^' + this._readEntity(token);
        break;
      // Add an "@lang" suffix for language tags
      case 'langcode':
        suffix = true;
        this._object += '@' + token.value.toLowerCase();
        break;
      }
      // If this literal was part of a list, write the item
      // (we could also check the context stack, but passing in a flag is faster)
      if (listItem)
        this._triple(this._subject, RDF_FIRST, this._object, this._graph);
      // Continue with the rest of the input
      if (suffix)
        return this._getContextEndReader();
      else {
        this._readCallback = this._getContextEndReader();
        return this._readCallback(token);
      }
    },
  
    // ### `_readFormulaTail` reads the end of a formula
    _readFormulaTail: function (token) {
      if (token.type !== '}')
        return this._readPunctuation(token);
  
      // Store the last triple of the formula
      if (this._subject !== null)
        this._triple(this._subject, this._predicate, this._object, this._graph);
  
      // Restore the parent context containing this formula
      this._restoreContext();
      // If the formula was the subject, continue reading the predicate.
      // If the formula was the object, read punctuation.
      return this._object === null ? this._readPredicate : this._getContextEndReader();
    },
  
    // ### `_readPunctuation` reads punctuation between triples or triple parts
    _readPunctuation: function (token) {
      var next, subject = this._subject, graph = this._graph,
          inversePredicate = this._inversePredicate;
      switch (token.type) {
      // A closing brace ends a graph
      case '}':
        if (this._graph === null)
          return this._error('Unexpected graph closing', token);
        if (this._n3Mode)
          return this._readFormulaTail(token);
        this._graph = null;
      // A dot just ends the statement, without sharing anything with the next
      case '.':
        this._subject = null;
        next = this._contextStack.length ? this._readSubject : this._readInTopContext;
        if (inversePredicate) this._inversePredicate = false;
        break;
      // Semicolon means the subject is shared; predicate and object are different
      case ';':
        next = this._readPredicate;
        break;
      // Comma means both the subject and predicate are shared; the object is different
      case ',':
        next = this._readObject;
        break;
      default:
        // An entity means this is a quad (only allowed if not already inside a graph)
        if (this._supportsQuads && this._graph === null && (graph = this._readEntity(token)) !== undefined) {
          next = this._readQuadPunctuation;
          break;
        }
        return this._error('Expected punctuation to follow "' + this._object + '"', token);
      }
      // A triple has been completed now, so return it
      if (subject !== null) {
        var predicate = this._predicate, object = this._object;
        if (!inversePredicate)
          this._triple(subject, predicate, object,  graph);
        else
          this._triple(object,  predicate, subject, graph);
      }
      return next;
    },
  
      // ### `_readBlankNodePunctuation` reads punctuation in a blank node
    _readBlankNodePunctuation: function (token) {
      var next;
      switch (token.type) {
      // Semicolon means the subject is shared; predicate and object are different
      case ';':
        next = this._readPredicate;
        break;
      // Comma means both the subject and predicate are shared; the object is different
      case ',':
        next = this._readObject;
        break;
      default:
        return this._error('Expected punctuation to follow "' + this._object + '"', token);
      }
      // A triple has been completed now, so return it
      this._triple(this._subject, this._predicate, this._object, this._graph);
      return next;
    },
  
    // ### `_readQuadPunctuation` reads punctuation after a quad
    _readQuadPunctuation: function (token) {
      if (token.type !== '.')
        return this._error('Expected dot to follow quad', token);
      return this._readInTopContext;
    },
  
    // ### `_readPrefix` reads the prefix of a prefix declaration
    _readPrefix: function (token) {
      if (token.type !== 'prefix')
        return this._error('Expected prefix to follow @prefix', token);
      this._prefix = token.value;
      return this._readPrefixIRI;
    },
  
    // ### `_readPrefixIRI` reads the IRI of a prefix declaration
    _readPrefixIRI: function (token) {
      if (token.type !== 'IRI')
        return this._error('Expected IRI to follow prefix "' + this._prefix + ':"', token);
      var prefixIRI = this._readEntity(token);
      this._prefixes[this._prefix] = prefixIRI;
      this._prefixCallback(this._prefix, prefixIRI);
      return this._readDeclarationPunctuation;
    },
  
    // ### `_readBaseIRI` reads the IRI of a base declaration
    _readBaseIRI: function (token) {
      if (token.type !== 'IRI')
        return this._error('Expected IRI to follow base declaration', token);
      this._setBase(this._base === null || absoluteIRI.test(token.value) ?
                    token.value : this._resolveIRI(token));
      return this._readDeclarationPunctuation;
    },
  
    // ### `_readNamedGraphLabel` reads the label of a named graph
    _readNamedGraphLabel: function (token) {
      switch (token.type) {
      case 'IRI':
      case 'blank':
      case 'prefixed':
        return this._readSubject(token), this._readGraph;
      case '[':
        return this._readNamedGraphBlankLabel;
      default:
        return this._error('Invalid graph label', token);
      }
    },
  
    // ### `_readNamedGraphLabel` reads a blank node label of a named graph
    _readNamedGraphBlankLabel: function (token) {
      if (token.type !== ']')
        return this._error('Invalid graph label', token);
      this._subject = '_:b' + blankNodeCount++;
      return this._readGraph;
    },
  
    // ### `_readDeclarationPunctuation` reads the punctuation of a declaration
    _readDeclarationPunctuation: function (token) {
      // SPARQL-style declarations don't have punctuation
      if (this._sparqlStyle) {
        this._sparqlStyle = false;
        return this._readInTopContext(token);
      }
  
      if (token.type !== '.')
        return this._error('Expected declaration to end with a dot', token);
      return this._readInTopContext;
    },
  
    // Reads a list of quantified symbols from a @forSome or @forAll statement
    _readQuantifierList: function (token) {
      var entity;
      switch (token.type) {
      case 'IRI':
      case 'prefixed':
        if ((entity = this._readEntity(token, true)) !== undefined)
          break;
      default:
        return this._error('Unexpected ' + token.type, token);
      }
      // Without explicit quantifiers, map entities to a quantified entity
      if (!this._explicitQuantifiers)
        this._quantified[entity] = this._quantifiedPrefix + blankNodeCount++;
      // With explicit quantifiers, output the reified quantifier
      else {
        // If this is the first item, start a new quantifier list
        if (this._subject === null)
          this._triple(this._graph || '', this._predicate,
                       this._subject = '_:b' + blankNodeCount++, QUANTIFIERS_GRAPH);
        // Otherwise, continue the previous list
        else
          this._triple(this._subject, RDF_REST,
                       this._subject = '_:b' + blankNodeCount++, QUANTIFIERS_GRAPH);
        // Output the list item
        this._triple(this._subject, RDF_FIRST, entity, QUANTIFIERS_GRAPH);
      }
      return this._readQuantifierPunctuation;
    },
  
    // Reads punctuation from a @forSome or @forAll statement
    _readQuantifierPunctuation: function (token) {
      // Read more quantifiers
      if (token.type === ',')
        return this._readQuantifierList;
      // End of the quantifier list
      else {
        // With explicit quantifiers, close the quantifier list
        if (this._explicitQuantifiers) {
          this._triple(this._subject, RDF_REST, RDF_NIL, QUANTIFIERS_GRAPH);
          this._subject = null;
        }
        // Read a dot
        this._readCallback = this._getContextEndReader();
        return this._readCallback(token);
      }
    },
  
    // ### `_getPathReader` reads a potential path and then resumes with the given function
    _getPathReader: function (afterPath) {
      this._afterPath = afterPath;
      return this._readPath;
    },
  
    // ### `_readPath` reads a potential path
    _readPath: function (token) {
      switch (token.type) {
      // Forward path
      case '!': return this._readForwardPath;
      // Backward path
      case '^': return this._readBackwardPath;
      // Not a path; resume reading where we left off
      default:
        var stack = this._contextStack, parent = stack.length && stack[stack.length - 1];
        // If we were reading a list item, we still need to output it
        if (parent && parent.type === 'item') {
          // The list item is the remaining subejct after reading the path
          var item = this._subject;
          // Switch back to the context of the list
          this._restoreContext();
          // Output the list item
          this._triple(this._subject, RDF_FIRST, item, this._graph);
        }
        return this._afterPath(token);
      }
    },
  
    // ### `_readForwardPath` reads a '!' path
    _readForwardPath: function (token) {
      var subject, predicate, object = '_:b' + blankNodeCount++;
      // The next token is the predicate
      if ((predicate = this._readEntity(token)) === undefined)
        return;
      // If we were reading a subject, replace the subject by the path's object
      if (this._predicate === null)
        subject = this._subject, this._subject = object;
      // If we were reading an object, replace the subject by the path's object
      else
        subject = this._object,  this._object  = object;
      // Emit the path's current triple and read its next section
      this._triple(subject, predicate, object, this._graph);
      return this._readPath;
    },
  
    // ### `_readBackwardPath` reads a '^' path
    _readBackwardPath: function (token) {
      var subject = '_:b' + blankNodeCount++, predicate, object;
      // The next token is the predicate
      if ((predicate = this._readEntity(token)) === undefined)
        return;
      // If we were reading a subject, replace the subject by the path's subject
      if (this._predicate === null)
        object = this._subject, this._subject = subject;
      // If we were reading an object, replace the subject by the path's subject
      else
        object = this._object,  this._object  = subject;
      // Emit the path's current triple and read its next section
      this._triple(subject, predicate, object, this._graph);
      return this._readPath;
    },
  
    // ### `_getContextEndReader` gets the next reader function at the end of a context
    _getContextEndReader: function () {
      var contextStack = this._contextStack;
      if (!contextStack.length)
        return this._readPunctuation;
  
      switch (contextStack[contextStack.length - 1].type) {
      case 'blank':
        return this._readBlankNodeTail;
      case 'list':
        return this._readListItem;
      case 'formula':
        return this._readFormulaTail;
      }
    },
  
    // ### `_triple` emits a triple through the callback
    _triple: function (subject, predicate, object, graph) {
      this._callback(null,
        { subject: subject, predicate: predicate, object: object, graph: graph || '' });
    },
  
    // ### `_error` emits an error message through the callback
    _error: function (message, token) {
      this._callback(new Error(message + ' on line ' + token.line + '.'));
    },
  
    // ### `_resolveIRI` resolves a relative IRI token against the base path,
    // assuming that a base path has been set and that the IRI is indeed relative
    _resolveIRI: function (token) {
      var iri = token.value;
      switch (iri[0]) {
      // An empty relative IRI indicates the base IRI
      case undefined: return this._base;
      // Resolve relative fragment IRIs against the base IRI
      case '#': return this._base + iri;
      // Resolve relative query string IRIs by replacing the query string
      case '?': return this._base.replace(/(?:\?.*)?$/, iri);
      // Resolve root-relative IRIs at the root of the base IRI
      case '/':
        // Resolve scheme-relative IRIs to the scheme
        return (iri[1] === '/' ? this._baseScheme : this._baseRoot) + this._removeDotSegments(iri);
      // Resolve all other IRIs at the base IRI's path
      default:
        return this._removeDotSegments(this._basePath + iri);
      }
    },
  
    // ### `_removeDotSegments` resolves './' and '../' path segments in an IRI as per RFC3986
    _removeDotSegments: function (iri) {
      // Don't modify the IRI if it does not contain any dot segments
      if (!dotSegments.test(iri))
        return iri;
  
      // Start with an imaginary slash before the IRI in order to resolve trailing './' and '../'
      var result = '', length = iri.length, i = -1, pathStart = -1, segmentStart = 0, next = '/';
  
      while (i < length) {
        switch (next) {
        // The path starts with the first slash after the authority
        case ':':
          if (pathStart < 0) {
            // Skip two slashes before the authority
            if (iri[++i] === '/' && iri[++i] === '/')
              // Skip to slash after the authority
              while ((pathStart = i + 1) < length && iri[pathStart] !== '/')
                i = pathStart;
          }
          break;
        // Don't modify a query string or fragment
        case '?':
        case '#':
          i = length;
          break;
        // Handle '/.' or '/..' path segments
        case '/':
          if (iri[i + 1] === '.') {
            next = iri[++i + 1];
            switch (next) {
            // Remove a '/.' segment
            case '/':
              result += iri.substring(segmentStart, i - 1);
              segmentStart = i + 1;
              break;
            // Remove a trailing '/.' segment
            case undefined:
            case '?':
            case '#':
              return result + iri.substring(segmentStart, i) + iri.substr(i + 1);
            // Remove a '/..' segment
            case '.':
              next = iri[++i + 1];
              if (next === undefined || next === '/' || next === '?' || next === '#') {
                result += iri.substring(segmentStart, i - 2);
                // Try to remove the parent path from result
                if ((segmentStart = result.lastIndexOf('/')) >= pathStart)
                  result = result.substr(0, segmentStart);
                // Remove a trailing '/..' segment
                if (next !== '/')
                  return result + '/' + iri.substr(i + 1);
                segmentStart = i + 1;
              }
            }
          }
        }
        next = iri[++i];
      }
      return result + iri.substring(segmentStart);
    },
  
    // ## Public methods
  
    // ### `parse` parses the N3 input and emits each parsed triple through the callback
    parse: function (input, tripleCallback, prefixCallback) {
      var self = this;
      // The read callback is the next function to be executed when a token arrives.
      // We start reading in the top context.
      this._readCallback = this._readInTopContext;
      this._sparqlStyle = false;
      this._prefixes = Object.create(null);
      this._prefixes._ = this._blankNodePrefix || '_:b' + blankNodePrefix++ + '_';
      this._prefixCallback = prefixCallback || noop;
      this._inversePredicate = false;
      this._quantified = Object.create(null);
  
      // Parse synchronously if no triple callback is given
      if (!tripleCallback) {
        var triples = [], error;
        this._callback = function (e, t) { e ? (error = e) : t && triples.push(t); };
        this._lexer.tokenize(input).every(function (token) {
          return self._readCallback = self._readCallback(token);
        });
        if (error) throw error;
        return triples;
      }
  
      // Parse asynchronously otherwise, executing the read callback when a token arrives
      this._callback = tripleCallback;
      this._lexer.tokenize(input, function (error, token) {
        if (error !== null)
          self._callback(error), self._callback = noop;
        else if (self._readCallback)
          self._readCallback = self._readCallback(token);
      });
    },
  };
  
  // The empty function
  function noop() {}
  
  // ## Exports
  module.exports = N3Parser;
  