Implemented rudimentary reduction scanner
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import * as CF from '@efforting.tech/data/field-configuration-factories';
|
||||
import * as CF from '@efforting.tech/schema/field-configuration-factories';
|
||||
|
||||
|
||||
export const Indention_Mode = new CF.symbol_set({
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { Reduction_Contract, FPR_Contract } from './contracts.mjs';
|
||||
|
||||
|
||||
import * as CF from '@efforting.tech/data/field-configuration-factories';
|
||||
import * as CF from '@efforting.tech/schema/field-configuration-factories';
|
||||
|
||||
|
||||
export const Reduction_Order = new CF.symbol_set({
|
||||
@@ -44,26 +42,38 @@ export class Reduction_Scanner {
|
||||
|
||||
case Reduction_Order.symbols.RULE_MAJOR:
|
||||
for (const rule of settings.rules) {
|
||||
for (let start_index=0; start_index < sequence.length; start_index++) {
|
||||
const match = rule.match(sequence, start_index);
|
||||
if (match) {
|
||||
rule.action(this, sequence, match);
|
||||
return true;
|
||||
}
|
||||
const match = rule.match(sequence);
|
||||
if (match) {
|
||||
//console.log('RULE_MAJOR', match);
|
||||
rule.action(this, sequence, match); //TODO: should rule.action be able to add additional checks? Though that probably dillutes responsibility and blurs interfaces
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
case Reduction_Order.symbols.POSITION_MAJOR:
|
||||
for (let start_index=0; start_index < sequence.length; start_index++) {
|
||||
for (const rule of settings.rules) {
|
||||
const match = rule.match(sequence, start_index);
|
||||
if (match) {
|
||||
rule.action(this, sequence, match);
|
||||
return true;
|
||||
|
||||
let best_match, best_rule;
|
||||
|
||||
for (const rule of settings.rules) {
|
||||
const match = rule.match(sequence);
|
||||
|
||||
if (match) {
|
||||
if (!best_match || best_match.match_start > match.match_start) {
|
||||
//TODO - early return if start of sequence
|
||||
best_match = match;
|
||||
best_rule = rule;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best_match) {
|
||||
//console.log('POSITION_MAJOR', best_match)
|
||||
best_rule.action(this, sequence, best_match); //TODO: should rule.action be able to add additional checks? Though that probably dillutes responsibility and blurs interfaces
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
default:
|
||||
throw new Error(`Unknown reduction order: ${this.reduction_order}`); //TODO: Force invalid configuration error
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Item_Unresolvable } from '@efforting.tech/errors';
|
||||
|
||||
//TODO: Should this be integrated with rules.mjs for predicates? Possibly a normalization step during rule insertion.
|
||||
|
||||
export class Abstract_Resolver {
|
||||
resolve(item, extra_info={}) {
|
||||
const result = this.resolve_handler(item, extra_info);
|
||||
|
||||
108
source/rule-processing/rules.mjs
Normal file
108
source/rule-processing/rules.mjs
Normal file
@@ -0,0 +1,108 @@
|
||||
|
||||
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});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as CF from '@efforting.tech/data/field-configuration-factories';
|
||||
import * as CF from '@efforting.tech/schema/field-configuration-factories';
|
||||
|
||||
|
||||
export const Object_Serialization_Strategy = new CF.Schema({
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { string_has_contents, indented_line_iterator } from '@efforting.tech/data/string-utilities';
|
||||
import * as CF from '@efforting.tech/data/field-configuration-factories';
|
||||
import { Text_Settings } from '@efforting.tech/data/string-utilities';
|
||||
import { Text_Settings, string_has_contents, indented_line_iterator } from '@efforting.tech/data/string-utilities';
|
||||
import * as CF from '@efforting.tech/schema/field-configuration-factories';
|
||||
|
||||
export const Text_Tree_Settings = new CF.Schema({
|
||||
//BUG - there is currently no way (I think) to put defaults into a sub schema - this should be fixed
|
||||
|
||||
Reference in New Issue
Block a user