Skip to main content
Module

x/ohm_js/src/Interval.js

A library and language for building parsers, interpreters, compilers, etc.
Go to Latest
File
'use strict';
// --------------------------------------------------------------------// Imports// --------------------------------------------------------------------
const {assert} = require('./common');const errors = require('./errors');const util = require('./util');
// --------------------------------------------------------------------// Private stuff// --------------------------------------------------------------------
function Interval(sourceString, startIdx, endIdx) { this.sourceString = sourceString; this.startIdx = startIdx; this.endIdx = endIdx;}
Interval.coverage = function(firstInterval, ...intervals) { let {startIdx, endIdx} = firstInterval; for (const interval of intervals) { if (interval.sourceString !== firstInterval.sourceString) { throw errors.intervalSourcesDontMatch(); } else { startIdx = Math.min(startIdx, interval.startIdx); endIdx = Math.max(endIdx, interval.endIdx); } } return new Interval(firstInterval.sourceString, startIdx, endIdx);};
Interval.prototype = { coverageWith(...intervals) { return Interval.coverage(...intervals, this); },
collapsedLeft() { return new Interval(this.sourceString, this.startIdx, this.startIdx); },
collapsedRight() { return new Interval(this.sourceString, this.endIdx, this.endIdx); },
getLineAndColumn() { return util.getLineAndColumn(this.sourceString, this.startIdx); },
getLineAndColumnMessage() { const range = [this.startIdx, this.endIdx]; return util.getLineAndColumnMessage(this.sourceString, this.startIdx, range); },
// Returns an array of 0, 1, or 2 intervals that represents the result of the // interval difference operation. minus(that) { if (this.sourceString !== that.sourceString) { throw errors.intervalSourcesDontMatch(); } else if (this.startIdx === that.startIdx && this.endIdx === that.endIdx) { // `this` and `that` are the same interval! return []; } else if (this.startIdx < that.startIdx && that.endIdx < this.endIdx) { // `that` splits `this` into two intervals return [ new Interval(this.sourceString, this.startIdx, that.startIdx), new Interval(this.sourceString, that.endIdx, this.endIdx), ]; } else if (this.startIdx < that.endIdx && that.endIdx < this.endIdx) { // `that` contains a prefix of `this` return [new Interval(this.sourceString, that.endIdx, this.endIdx)]; } else if (this.startIdx < that.startIdx && that.startIdx < this.endIdx) { // `that` contains a suffix of `this` return [new Interval(this.sourceString, this.startIdx, that.startIdx)]; } else { // `that` and `this` do not overlap return [this]; } },
// Returns a new Interval that has the same extent as this one, but which is relative // to `that`, an Interval that fully covers this one. relativeTo(that) { if (this.sourceString !== that.sourceString) { throw errors.intervalSourcesDontMatch(); } assert( this.startIdx >= that.startIdx && this.endIdx <= that.endIdx, 'other interval does not cover this one' ); return new Interval( this.sourceString, this.startIdx - that.startIdx, this.endIdx - that.startIdx ); },
// Returns a new Interval which contains the same contents as this one, // but with whitespace trimmed from both ends. (This only makes sense when // the input stream is a string.) trimmed() { const {contents} = this; const startIdx = this.startIdx + contents.match(/^\s*/)[0].length; const endIdx = this.endIdx - contents.match(/\s*$/)[0].length; return new Interval(this.sourceString, startIdx, endIdx); },
subInterval(offset, len) { const newStartIdx = this.startIdx + offset; return new Interval(this.sourceString, newStartIdx, newStartIdx + len); },};
Object.defineProperties(Interval.prototype, { contents: { get() { if (this._contents === undefined) { this._contents = this.sourceString.slice(this.startIdx, this.endIdx); } return this._contents; }, enumerable: true, }, length: { get() { return this.endIdx - this.startIdx; }, enumerable: true, },});
// --------------------------------------------------------------------// Exports// --------------------------------------------------------------------
module.exports = Interval;