108 lines
2.7 KiB
JavaScript
108 lines
2.7 KiB
JavaScript
|
|
const ABORT_SEQUENCE = Symbol('ABORT_SEQUENCE');
|
|
|
|
export class Abstract_Item_Condition {
|
|
match(sequence, context={}) {
|
|
return this.match_value(sequence[context.start_index ?? 0], context);
|
|
}
|
|
|
|
}
|
|
|
|
export class Abstract_Sequence_Condition {
|
|
}
|
|
|
|
export class Match {
|
|
constructor(rule, value, context) {
|
|
Object.assign(this, { rule, value, ...context });
|
|
}
|
|
}
|
|
|
|
export class Predicate extends Abstract_Item_Condition {
|
|
constructor(predicate) {
|
|
super();
|
|
Object.assign(this, { predicate });
|
|
}
|
|
|
|
match_value(item, context={}) {
|
|
if (this.predicate(item, context)) {
|
|
const si = context.start_index ?? 0;
|
|
return new Match(this, item, { ...context, match_start: si, match_end: si });
|
|
};
|
|
}
|
|
|
|
}
|
|
|
|
export class Type_Is extends Abstract_Item_Condition {
|
|
constructor(type) {
|
|
super();
|
|
Object.assign(this, { type });
|
|
}
|
|
|
|
match_value(item, context={}) {
|
|
if (typeof item === this.type) {
|
|
const si = context.start_index ?? 0;
|
|
return new Match(this, item, { ...context, match_start: si, match_end: si });
|
|
};
|
|
}
|
|
|
|
}
|
|
|
|
export class Strict_Equality extends Abstract_Item_Condition {
|
|
constructor(value) {
|
|
super();
|
|
Object.assign(this, { value });
|
|
}
|
|
|
|
match_value(item, context={}) {
|
|
if (item === this.value) {
|
|
const si = context.start_index ?? 0;
|
|
return new Match(this, item, { ...context, match_start: si, match_end: si });
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
export class Sequence_Condition extends Abstract_Sequence_Condition {
|
|
constructor(sequence) {
|
|
super();
|
|
Object.assign(this, { sequence });
|
|
}
|
|
|
|
match(sequence, context={}) {
|
|
//TODO: For anchors we need anchor_start_index and anchor_end_index (compare with regexp ^ and $)
|
|
const start_index = context.start_index ?? 0;
|
|
const end_index = context.end_index ?? sequence.length - 1;
|
|
|
|
const match_from = (sequence_index, pattern_index=0) => {
|
|
|
|
if (pattern_index === this.sequence.length) {
|
|
return [];
|
|
}
|
|
|
|
//TODO - There are plenty of optimizations to be implemented here but we must be suer they are correct - we will start naively
|
|
const sub_condition = this.sequence[pattern_index];
|
|
const sub_match = sub_condition.match(sequence, { ...context, start_index: sequence_index });
|
|
|
|
//console.log('match_from', {sequence_index, pattern_index, sub_condition, sub_match})
|
|
|
|
if (sub_match) {
|
|
const remaining = match_from(sub_match.match_end + 1, pattern_index + 1);
|
|
if (remaining) {
|
|
return [sub_match, ...remaining];
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
for (let i=start_index; i<=end_index; i++) {
|
|
const m = match_from(i);
|
|
if (m) {
|
|
// NOTE: If result is empty array which can be a positive match, match_start and match_end will be undefined which is by design
|
|
return new Match(this, m, { match_start: m.at(0)?.match_start, match_end: m.at(-1)?.match_end, ...context});
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
} |