UNPKG

string-reader

Version:

Reads string based on regular expression that represents delimiter

122 lines (115 loc) 3.85 kB
"use strict"; const _content = new WeakMap(); const _position = new WeakMap(); const _stack = new WeakMap(); class StringReader { constructor(content) { this.content = content; _stack[this] = []; } set content (value) { _content[this] = value ? (typeof(value) === 'string' ? value : value.toString()) : ''; _position[this] = 0; return _content; } get content () { return _content[this]; } set position (value) { return _position[this] = Math.max(0, Math.min(this.length, value)); } get position () { return _position[this]; } get length () { return this.content.length } get end () { return this.position == this.length } push() { _stack[this].push(this.position); } pop(holdPosition) { var from = _stack[this].pop(); var to = this.position; if (!holdPosition) this.position = from; return this.content.substring(Math.min(from, to), Math.max(from, to)) } peek(count, errorThrown) { if (count < 0) { if (errorThrown && this.position + count < 0) throw errorThrown; var i = Math.max(0, this.position + count); var c = Math.min(this.position, -count); return this.content.substr(i, c); } else { var left = this.length - this.position; if (errorThrown && left < count) throw errorThrown; return this.content.substr(this.position, Math.min(left, count)); } } read(count, errorThrown) { var peek = this.peek(count, errorThrown); this.position = count < 0 ? Math.max(0, this.position + count) : Math.min(this.content.length, this.position + count) return peek; } peekTo(delimiter, holdDelimiter, errorThrown) { var r = (delimiter instanceof RegExp ? (delimiter.global ? delimiter : new RegExp(delimiter.source, ['g', delimiter.multiline ? 'm' : '', delimiter.ignoreCase ? 'i' : '' ].join('')) ) : new RegExp(delimiter, 'g')); r.lastIndex = this.position; var m = r.exec(this.content); if (m && m[0].length == 0) throw `delimiter never be a pattern that matches empty string: ${delimiter}`; if (!m && errorThrown) throw errorThrown; return new TaggedString( this.content.substring(this.position, m ? ( holdDelimiter ? m.index + m[0].length : m.index ) : this.content.length ), { reader: this, position: this.position, delimiter: m }); } readTo(delimiter, holdDelimiter, errorThrown) { var peek = this.peekTo(delimiter, holdDelimiter, errorThrown); this.position += (holdDelimiter ? peek.length : ( peek.delimiter ? peek.length + peek.delimiter[0].length : peek.length ) ); return peek; } peekLine(errorThrown) { return this.peekTo(/\r?\n|\r/, false, errorThrown).toString(); } readLine(errorThrown) { return this.readTo(/\r?\n|\r/, false, errorThrown).toString(); } peakToEnd(errorThrown) { if (errorThrown && this.end) throw errorThrown; return this.content.slice(this.position); } readToEnd(errorThrown) { var peak = this.peakToEnd(errorThrown); this.position = this.length; return peak; } } class TaggedString extends String { constructor(thing, tag) { super(thing); Object.defineProperty(this, 'tag', { value: tag, writable: false }); for (var p in tag) if (!Object.getOwnPropertyDescriptor(this, p)) Object.defineProperty(this, p, { value: tag[p], writable: false }); } } module.exports = StringReader;