var util = require("util"); var StringSource = module.exports = function(string, description) { var self = { asString: function() { return string; }, range: function(startIndex, endIndex) { return new StringSourceRange(string, description, startIndex, endIndex); } }; return self; }; var StringSourceRange = function(string, description, startIndex, endIndex) { this._string = string; this._description = description; this._startIndex = startIndex; this._endIndex = endIndex; }; StringSourceRange.prototype.to = function(otherRange) { // TODO: Assert that tokens are the same across both iterators return new StringSourceRange(this._string, this._description, this._startIndex, otherRange._endIndex); }; StringSourceRange.prototype.describe = function() { var position = this._position(); var description = this._description ? this._description + "\n" : ""; return util.format("%sLine number: %s\nCharacter number: %s", description, position.lineNumber, position.characterNumber); }; StringSourceRange.prototype.lineNumber = function() { return this._position().lineNumber; }; StringSourceRange.prototype.characterNumber = function() { return this._position().characterNumber; }; StringSourceRange.prototype._position = function() { var self = this; var index = 0; var nextNewLine = function() { return self._string.indexOf("\n", index); }; var lineNumber = 1; while (nextNewLine() !== -1 && nextNewLine() < this._startIndex) { index = nextNewLine() + 1; lineNumber += 1; } var characterNumber = this._startIndex - index + 1; return {lineNumber: lineNumber, characterNumber: characterNumber}; };