UNPKG

molstar

Version:

A comprehensive macromolecular library.

501 lines (500 loc) 16.5 kB
"use strict"; /** * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> * @author Koya Sakuma <koya.sakuma.work@gmail.com> ** * Adapted from Parsimmon (https://github.com/jneen/parsimmon) * Copyright (c) 2011-present J. Adkisson (http://jneen.net). **/ Object.defineProperty(exports, "__esModule", { value: true }); exports.MonadicParser = void 0; class MonadicParser { constructor(_) { this._ = _; } parse(input) { const result = this.skip(MonadicParser.eof)._(input, 0); if (result.status) { return { success: true, value: result.value }; } return { success: false, index: makeLineColumnIndex(input, result.furthest), expected: result.expected }; } ; tryParse(str) { const result = this.parse(str); if (result.success) { return result.value; } else { const msg = formatError(str, result); const err = new Error(msg); throw err; } } or(alternative) { return MonadicParser.alt(this, alternative); } trim(parser) { return this.wrap(parser, parser); } wrap(leftParser, rightParser) { return seqPick(1, typeof leftParser === 'string' ? MonadicParser.string(leftParser) : leftParser, this, typeof rightParser === 'string' ? MonadicParser.string(rightParser) : rightParser); } thru(wrapper) { return wrapper(this); } then(next) { return seqPick(1, this, next); } many() { return new MonadicParser((input, i) => { const accum = []; let result = void 0; while (true) { result = mergeReplies(this._(input, i), result); if (result.status) { if (i === result.index) { throw new Error('infinite loop detected in .many() parser --- calling .many() on a parser which can accept zero characters is usually the cause'); } i = result.index; accum.push(result.value); } else { return mergeReplies(makeSuccess(i, accum), result); } } }); } ; times(min, _max) { const max = typeof _max === 'undefined' ? min : _max; return new MonadicParser((input, i) => { const accum = []; let result = void 0; let prevResult = void 0; let times; for (times = 0; times < min; times++) { result = this._(input, i); prevResult = mergeReplies(result, prevResult); if (result.status) { i = result.index; accum.push(result.value); } else { return prevResult; } } for (; times < max; times += 1) { result = this._(input, i); prevResult = mergeReplies(result, prevResult); if (result.status) { i = result.index; accum.push(result.value); } else { break; } } return mergeReplies(makeSuccess(i, accum), prevResult); }); } ; result(res) { return this.map(() => res); } ; atMost(n) { return this.times(0, n); } ; atLeast(n) { return MonadicParser.seq(this.times(n), this.many()).map(r => [...r[0], ...r[1]]); } ; map(f) { return new MonadicParser((input, i) => { const result = this._(input, i); if (!result.status) { return result; } return mergeReplies(makeSuccess(result.index, f(result.value)), result); }); } skip(next) { return seqPick(0, this, next); } mark() { return MonadicParser.seq(MonadicParser.index, this, MonadicParser.index).map(r => ({ start: r[0], value: r[1], end: r[2] })); } node(name) { return MonadicParser.seq(MonadicParser.index, this, MonadicParser.index).map(r => ({ name, start: r[0], value: r[1], end: r[2] })); } ; sepBy(separator) { return MonadicParser.sepBy(this, separator); } sepBy1(separator) { return MonadicParser.sepBy1(this, separator); } lookahead(x) { return this.skip(MonadicParser.lookahead(x)); } ; notFollowedBy(x) { return this.skip(MonadicParser.notFollowedBy(x)); } ; desc(expected) { return new MonadicParser((input, i) => { const reply = this._(input, i); if (!reply.status) { reply.expected = [expected]; } return reply; }); } ; fallback(result) { return this.or(MonadicParser.succeed(result)); } ; ap(other) { return MonadicParser.seq(other, this).map(([f, x]) => f(x)); } ; chain(f) { return new MonadicParser((input, i) => { const result = this._(input, i); if (!result.status) { return result; } const nextParser = f(result.value); return mergeReplies(nextParser._(input, result.index), result); }); } ; } exports.MonadicParser = MonadicParser; (function (MonadicParser) { function seqMap(a, b, c) { const args = [].slice.call(arguments); if (args.length === 0) { throw new Error('seqMap needs at least one argument'); } const mapper = args.pop(); assertFunction(mapper); return seq.apply(null, args).map(function (results) { return mapper.apply(null, results); }); } MonadicParser.seqMap = seqMap; function createLanguage(parsers) { const language = {}; for (const key of Object.keys(parsers)) { (function (key) { language[key] = lazy(() => parsers[key](language)); })(key); } return language; } MonadicParser.createLanguage = createLanguage; function seq(...parsers) { const numParsers = parsers.length; return new MonadicParser((input, index) => { let result; const accum = new Array(numParsers); let i = index; for (let j = 0; j < numParsers; j++) { result = mergeReplies(parsers[j]._(input, i), result); if (!result.status) { return result; } accum[j] = result.value; i = result.index; } return mergeReplies(makeSuccess(i, accum), result); }); } MonadicParser.seq = seq; function alt(...parsers) { const numParsers = parsers.length; if (numParsers === 0) { return fail('zero alternates'); } return new MonadicParser((input, i) => { let result; for (let j = 0; j < parsers.length; j++) { result = mergeReplies(parsers[j]._(input, i), result); if (result.status) { return result; } } return result; }); } MonadicParser.alt = alt; function sepBy(parser, separator) { return sepBy1(parser, separator).or(succeed([])); } MonadicParser.sepBy = sepBy; function sepBy1(parser, separator) { const pairs = separator.then(parser).many(); return seq(parser, pairs).map(r => [r[0], ...r[1]]); } MonadicParser.sepBy1 = sepBy1; function string(str) { const expected = `'${str}'`; if (str.length === 1) { const code = str.charCodeAt(0); return new MonadicParser((input, i) => input.charCodeAt(i) === code ? makeSuccess(i + 1, str) : makeFailure(i, expected)); } return new MonadicParser((input, i) => { const j = i + str.length; if (input.slice(i, j) === str) return makeSuccess(j, str); else return makeFailure(i, expected); }); } MonadicParser.string = string; function flags(re) { const s = '' + re; return s.slice(s.lastIndexOf('/') + 1); } function anchoredRegexp(re) { return RegExp('^(?:' + re.source + ')', flags(re)); } function regexp(re, group = 0) { const anchored = anchoredRegexp(re); const expected = '' + re; return new MonadicParser((input, i) => { const match = anchored.exec(input.slice(i)); if (match) { if (0 <= group && group <= match.length) { const fullMatch = match[0]; const groupMatch = match[group]; return makeSuccess(i + fullMatch.length, groupMatch); } const message = `invalid match group (0 to ${match.length}) in ${expected}`; return makeFailure(i, message); } return makeFailure(i, expected); }); } MonadicParser.regexp = regexp; function succeed(value) { return new MonadicParser((input, i) => makeSuccess(i, value)); } MonadicParser.succeed = succeed; function fail(expected) { return new MonadicParser((input, i) => makeFailure(i, expected)); } MonadicParser.fail = fail; function lookahead(x) { if (isParser(x)) { return new MonadicParser((input, i) => { const result = x._(input, i); if (result.status) { result.index = i; result.value = null; } return result; }); } else if (typeof x === 'string') { return lookahead(string(x)); } else if (x instanceof RegExp) { return lookahead(regexp(x)); } throw new Error('not a string, regexp, or parser: ' + x); } MonadicParser.lookahead = lookahead; function notFollowedBy(parser) { return new MonadicParser((input, i) => { const result = parser._(input, i); return result.status ? makeFailure(i, 'not "' + input.slice(i, result.index) + '"') : makeSuccess(i, null); }); } MonadicParser.notFollowedBy = notFollowedBy; function test(predicate) { return new MonadicParser((input, i) => { const char = input.charAt(i); if (i < input.length && predicate(char)) { return makeSuccess(i + 1, char); } else { return makeFailure(i, 'a character ' + predicate); } }); } MonadicParser.test = test; function oneOf(str) { return test(ch => str.indexOf(ch) >= 0); } MonadicParser.oneOf = oneOf; function noneOf(str) { return test(ch => str.indexOf(ch) < 0); } MonadicParser.noneOf = noneOf; function range(begin, end) { return test(ch => begin <= ch && ch <= end).desc(begin + '-' + end); } MonadicParser.range = range; function takeWhile(predicate) { return new MonadicParser((input, i) => { let j = i; while (j < input.length && predicate(input.charAt(j))) { j++; } return makeSuccess(j, input.slice(i, j)); }); } MonadicParser.takeWhile = takeWhile; function lazy(f) { const parser = new MonadicParser((input, i) => { const a = f()._; parser._ = a; return a(input, i); }); return parser; } MonadicParser.lazy = lazy; function empty() { return fail('empty'); } MonadicParser.empty = empty; MonadicParser.index = new MonadicParser(function (input, i) { return makeSuccess(i, makeLineColumnIndex(input, i)); }); MonadicParser.anyChar = new MonadicParser((input, i) => { if (i >= input.length) { return makeFailure(i, 'any character'); } return makeSuccess(i + 1, input.charAt(i)); }); MonadicParser.all = new MonadicParser(function (input, i) { return makeSuccess(input.length, input.slice(i)); }); MonadicParser.eof = new MonadicParser(function (input, i) { if (i < input.length) { return makeFailure(i, 'EOF'); } return makeSuccess(i, null); }); MonadicParser.digit = regexp(/[0-9]/).desc('a digit'); MonadicParser.digits = regexp(/[0-9]*/).desc('optional digits'); MonadicParser.letter = regexp(/[a-z]/i).desc('a letter'); MonadicParser.letters = regexp(/[a-z]*/i).desc('optional letters'); MonadicParser.optWhitespace = regexp(/\s*/).desc('optional whitespace'); MonadicParser.whitespace = regexp(/\s+/).desc('whitespace'); MonadicParser.cr = string('\r'); MonadicParser.lf = string('\n'); MonadicParser.crlf = string('\r\n'); MonadicParser.newline = alt(MonadicParser.crlf, MonadicParser.lf, MonadicParser.cr).desc('newline'); MonadicParser.end = alt(MonadicParser.newline, MonadicParser.eof); function of(value) { return succeed(value); } MonadicParser.of = of; function regex(re) { return regexp(re); } MonadicParser.regex = regex; })(MonadicParser || (exports.MonadicParser = MonadicParser = {})); function seqPick(idx, ...parsers) { const numParsers = parsers.length; return new MonadicParser((input, index) => { let result; let picked; let i = index; for (let j = 0; j < numParsers; j++) { result = mergeReplies(parsers[j]._(input, i), result); if (!result.status) { return result; } if (idx === j) picked = result.value; i = result.index; } return mergeReplies(makeSuccess(i, picked), result); }); } function makeSuccess(index, value) { return { status: true, index, value }; } function makeFailure(index, expected) { return { status: false, furthest: index, expected: [expected] }; } function mergeReplies(result, last) { if (!last || result.status || last.status || result.furthest > last.furthest) { return result; } const expected = result.furthest === last.furthest ? unsafeUnion(result.expected, last.expected) : last.expected; return { status: result.status, furthest: last.furthest, expected }; } function makeLineColumnIndex(input, i) { const lines = input.slice(0, i).split('\n'); // Note that unlike the character offset, the line and column offsets are // 1-based. const lineWeAreUpTo = lines.length; const columnWeAreUpTo = lines[lines.length - 1].length + 1; return { offset: i, line: lineWeAreUpTo, column: columnWeAreUpTo }; } function formatExpected(expected) { if (expected.length === 1) { return expected[0]; } return 'one of ' + expected.join(', '); } function formatGot(input, error) { const index = error.index; const i = index.offset; if (i === input.length) { return ', got the end of the input'; } const prefix = i > 0 ? '\'...' : '\''; const suffix = input.length - i > 12 ? '...\'' : '\''; return ` at line ${index.line} column ${index.column}, got ${prefix}${input.slice(i, i + 12)}${suffix}`; } function formatError(input, error) { return `expected ${formatExpected(error.expected)}${formatGot(input, error)}`; } function unsafeUnion(xs, ys) { const xn = xs.length; const yn = ys.length; if (xn === 0) return ys; else if (yn === 0) return xs; const set = new Set(); const ret = []; for (let i = 0; i < xn; i++) { if (!set.has(xs[i])) { ret[ret.length] = xs[i]; set.add(xs[i]); } } for (let i = 0; i < yn; i++) { if (!set.has(ys[i])) { ret[ret.length] = ys[i]; set.add(ys[i]); } } ret.sort(); return ret; } function isParser(obj) { return obj instanceof MonadicParser; } function assertFunction(x) { if (typeof x !== 'function') { throw new Error('not a function: ' + x); } }