104 lines
3.1 KiB
JavaScript
104 lines
3.1 KiB
JavaScript
import { Reduction_Contract, FPR_Contract } from './contracts.mjs';
|
|
|
|
|
|
import * as CF from '@efforting.tech/data/field-configuration-factories';
|
|
|
|
|
|
export const Reduction_Order = new CF.symbol_set({
|
|
RULE_MAJOR: 'For each rule, scan the full sequence',
|
|
POSITION_MAJOR: 'For each item in the sequence, try all rules',
|
|
}, 'Reduction ordering');
|
|
|
|
|
|
export const Reduction_Settings = new CF.Schema({
|
|
reduction_order: CF.symbol(Reduction_Order, 'RULE_MAJOR'),
|
|
reduction_contract: CF.instance(Reduction_Contract, 'The reduction contract defines the implementation of the reduction scanner'),
|
|
rules: CF.factory(() => []),
|
|
}, 'Reduction settings');
|
|
|
|
//TODO: Consider whether we can implement some hierarchical sub schema, like classes - we could of course reuse things by defining and object and spread it in here
|
|
export const FP_Reduction_Settings = new CF.Schema({
|
|
reduction_order: CF.symbol(Reduction_Order, 'RULE_MAJOR'),
|
|
reduction_contract: CF.instance(FPR_Contract, 'The reduction contract defines the implementation of the reduction scanner'),
|
|
rules: CF.factory(() => []),
|
|
}, 'Fixed point reduction settings');
|
|
|
|
|
|
|
|
export class Reduction_Scanner {
|
|
|
|
static settings_schema = Reduction_Settings;
|
|
|
|
constructor(settings, use_lifecycle_cb=true) {
|
|
settings = this.constructor.settings_schema.load(settings);
|
|
Object.assign(this, { settings });
|
|
this.clear_transform_state();
|
|
if (use_lifecycle_cb) {
|
|
settings.reduction_contract.on_create_scanner(this);
|
|
}
|
|
}
|
|
|
|
perform_reduction(sequence) {
|
|
const { settings } = this;
|
|
switch (settings.reduction_order) {
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
default:
|
|
throw new Error(`Unknown reduction order: ${this.reduction_order}`); //TODO: Force invalid configuration error
|
|
|
|
}
|
|
|
|
}
|
|
|
|
clear_transform_state() {
|
|
this.last_reduction_status = null;
|
|
this.reductions_attempted = 0;
|
|
this.reductions_made = 0;
|
|
}
|
|
|
|
transform(sequence) {
|
|
const { settings } = this;
|
|
const reduction_contract = settings.reduction_contract;
|
|
this.clear_transform_state();
|
|
reduction_contract.on_start_transform(this, sequence);
|
|
|
|
while (!reduction_contract.check_if_done(this, sequence)) {
|
|
reduction_contract.assert_readiness(this, sequence);
|
|
this.last_reduction_status = this.perform_reduction(sequence);
|
|
this.reductions_attempted++;
|
|
if (this.last_reduction_status) {
|
|
this.reductions_made++;
|
|
reduction_contract.on_reduction_made(this, sequence);
|
|
}
|
|
reduction_contract.on_reduction_attempted(this, sequence);
|
|
}
|
|
|
|
reduction_contract.on_end_transform(this, sequence);
|
|
return sequence;
|
|
}
|
|
|
|
}
|
|
|