tell-me-when
Version:
human relative date and time parser
218 lines (208 loc) • 5.67 kB
JavaScript
import { ParseNode } from "./ParseNode.mjs";
export class GrammarNode {
parseAs(parseAs) {
return new ParseAsNode(this, parseAs);
}
/**
* Matches this node once or zero times
*/
maybe() {
return new MaybeNode(this);
}
/**
* Matches this node or the alternate node
*/
or(alternate) {
return new OrNode(this, alternate);
}
/**
* Matches one of the given nodes. The first option to parse successfully wins
*/
static oneOf(...options) {
return new OrNode(...options.map(GrammarNode.toGrammarNode));
}
/**
* Matches one of the given nodes. Tries all of the options, and the one that
* successfully parses the farthest in the input wins
*/
static longestOf(...options) {
return new LongestOfNode(...options.map(GrammarNode.toGrammarNode));
}
/**
* Matches this node repeated the given number of times
*/
/**
* Matches this node repeated between min and max (inclusive) times
*/
repeat(countOrMin, max) {
return new RepeatNode(this, max != null ? [countOrMin, max] : countOrMin);
}
static toGrammarNode(factor) {
return typeof factor === 'function' ? new GrammarNodeRef(factor) : factor instanceof GrammarNode ? factor : new TokenNode(factor);
}
/**
* Matches the given string or regular expression
*/
static token(token) {
return new TokenNode(token);
}
/**
* Matches the given nodes in sequence
*/
static group(...sequence) {
return new GroupNode(undefined, ...sequence.map(GrammarNode.toGrammarNode));
}
/**
* Creates a named group that matches the given nodes in sequence.
* Same as {@link group} but the {@link ParseNode} returned by {@link parse}
* will have the given name.
*/
static named(name, ...sequence) {
return new GroupNode(name, ...sequence.map(GrammarNode.toGrammarNode));
}
static negativeLookahead(...sequence) {
return new NegativeLookaheadNode(sequence.length === 1 ? GrammarNode.toGrammarNode(sequence[0]) : GrammarNode.group(...sequence));
}
}
export class GrammarNodeRef extends GrammarNode {
constructor(ref) {
super();
this.ref = ref;
}
parse(state) {
return this.ref().parse(state);
}
}
export class TokenNode extends GrammarNode {
constructor(token) {
super();
this.token = token;
}
parse(state) {
const match = state.match(this.token);
if (match) {
state.index = match.index + match[0].length;
return new ParseNode(undefined, match.index, state.index);
}
return ParseNode.error(state.index);
}
}
export class MaybeNode extends GrammarNode {
constructor(node) {
super();
this.node = node;
}
parse(state) {
const parsed = this.node.parse(state);
return parsed.isError ? ParseNode.empty(state.index) : parsed;
}
}
export class RepeatNode extends GrammarNode {
constructor(node, count) {
super();
this.node = node;
this.count = count;
}
parse(state) {
const startIndex = state.index;
const children = [];
for (let i = 0; i < (Array.isArray(this.count) ? this.count[1] : this.count); i++) {
const parsed = this.node.parse(state);
if (parsed.isError) {
if (i < (Array.isArray(this.count) ? this.count[0] : this.count)) {
state.index = startIndex;
return parsed;
}
break;
}
if (!parsed.isEmpty) children.push(parsed);
}
return new ParseNode(undefined, startIndex, state.index, children);
}
}
export class OrNode extends GrammarNode {
options;
constructor(...options) {
super();
this.options = options;
}
parse(state) {
const startIndex = state.index;
for (const option of this.options) {
const parsed = option.parse(state);
if (!parsed.isError) return parsed;
}
state.index = startIndex;
return ParseNode.error(startIndex);
}
}
export class LongestOfNode extends GrammarNode {
options;
constructor(...options) {
super();
this.options = options;
}
parse(state) {
const startIndex = state.index;
let best = ParseNode.error(startIndex);
for (const option of this.options) {
state.index = startIndex;
const parsed = option.parse(state);
if (!parsed.isError && (best.isError || parsed.to > best.to)) {
best = parsed;
if (parsed.to === state.end) break;
}
}
state.index = best.isError ? startIndex : best.to;
return best;
}
}
export class GroupNode extends GrammarNode {
factors;
constructor(name, ...factors) {
super();
this.name = name;
this.factors = factors;
}
parse(state) {
const startIndex = state.index;
const children = [];
for (const factor of this.factors) {
const parsed = factor.parse(state);
if (parsed.isError) {
state.index = startIndex;
return parsed;
}
if (!parsed.isEmpty) children.push(parsed);
}
return new ParseNode(this.name, startIndex, state.index, children);
}
}
export class NegativeLookaheadNode extends GrammarNode {
constructor(node) {
super();
this.node = node;
}
parse(state) {
const {
index
} = state;
const parsed = this.node.parse(state);
if (!parsed.isError) return ParseNode.error(index);
state.index = index;
return ParseNode.empty(index);
}
}
export class ParseAsNode extends GrammarNode {
constructor(node, parseAsClass) {
super();
this.node = node;
this.parseAsClass = parseAsClass;
}
parse(state) {
const parsed = this.node.parse(state);
if (parsed.isError) return parsed;
return new this.parseAsClass(parsed);
}
}
//# sourceMappingURL=GrammarNode.mjs.map