JSFiddle - React, Tailwind, and code Playground
HTML
<html>
<head>
<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
</head>
<body>
</body>
</html>
JavaScript
class MatchError extends Error {}
class Range {
constructor(text, position = 0) {
this.text = text;
this.position = position;
}
get remainder() {
return this.text.substr(this.position);
}
extractRegExp(pattern) {
let matches = this.remainder.match(pattern);
if (!matches || matches.index !== 0) throw new MatchError(`Mismatch parsing "${this.remainder.substr(0, 10)}..." with "${pattern}"`);
this.position += matches[0].length; /**/
console.debug(`Matched '${matches[0]}'`)
return matches[0];
}
extractString(string) {
if (!this.remainder.startsWith(string)) throw new MatchError(`Mismatch parsing "${this.remainder.substr(0, 10)}..." with "${string}"`);
this.position += string.length; /**/
console.debug(`Matched '${string}'`)
return string;
}
get copy() {
this.lastCopy = new Range(this.text, this.position);
return this.lastCopy;
}
commitLastCopy = () => {
this.position = this.lastCopy.position;
}
}
class Choice extends Array {
map(parse) { //console.log(`Choosing between`)
for (let option of this) { //console.log(option)
try {
return parse(option);
} catch (e) {
if (e.constructor !== MatchError) throw e; /**/
console.debug(e, ` when choosing`);
}
}
throw new Error(`Failed all choices ${this}`);
}
}
function either(...x) {
return new Choice().concat(x);
}
class Repetition {
constructor(value) {
this.value = value;
}
map(parse) {
try {
return [parse(this.value)].concat(this.map(parse));
} catch (e) {
if (e.constructor !== MatchError) throw e; /**/
console.debug(e, ` when iterating`);
return [];
}
}
}
function many(x) {
return new...