import type { Predicate } from "../Function";
import type { Stack } from "../support/Stack";
import * as S from "../support/Stack";
import type { Filter, FreeMonoid, Map } from "./model";
/*
* -------------------------------------------
* FreeSemigroup Destructors
* -------------------------------------------
*/
/**
* @category Destructors
* @since 1.0.0
*/
export const fold_ = (
f: FreeMonoid,
patterns: {
Empty: () => R;
Element: (value: A) => R;
Filter: (fa: FreeMonoid, f: Predicate) => R;
Map: (fa: FreeMonoid, f: (a: A) => A) => R;
Combine: (l: FreeMonoid, r: FreeMonoid) => R;
}
): R => {
switch (f._tag) {
case "Empty":
return patterns.Empty();
case "Element":
return patterns.Element(f.value);
case "Combine":
return patterns.Combine(f.left, f.right);
case "Filter":
return patterns.Filter(f.fa, f.f);
case "Map":
return patterns.Map(f.fa, f.f);
}
};
/**
* @category Destructors
* @since 1.0.0
*/
export const fold = (patterns: {
Empty: () => R;
Element: (value: A) => R;
Filter: (fa: FreeMonoid, f: Predicate) => R;
Map: (fa: FreeMonoid, f: (a: A) => A) => R;
Combine: (l: FreeMonoid, r: FreeMonoid) => R;
}) => (f: FreeMonoid): R => fold_(f, patterns);
type Ops = Filter | Map;
/**
* https://github.com/Matechs-Garage/matechs-effect/blob/master/packages/system/src/FreeAssociative/index.ts
*
* @category Destructors
* @since 1.0.0
*/
export const toArray = (fs: FreeMonoid): ReadonlyArray => {
const as: Array = [];
let current: FreeMonoid | undefined = fs;
let stack: Stack> | undefined = undefined;
let ops: Stack> | undefined = undefined;
while (current !== undefined) {
switch (current._tag) {
case "Empty": {
current = undefined;
break;
}
case "Element": {
if (ops !== undefined) {
let op: Stack> | undefined = ops;
let drop = false;
let a = current.value;
while (op !== undefined && !drop) {
switch (op.value._tag) {
case "Filter": {
if (!op.value.f(a)) {
drop = true;
}
break;
}
case "Map": {
a = op.value.f(a);
break;
}
}
op = op.previous;
}
if (!drop) as.push(a);
} else {
as.push(current.value);
}
current = undefined;
break;
}
case "Filter": {
ops = S.stack(current, ops);
current = current.fa;
break;
}
case "Map": {
ops = S.stack(current, ops);
current = current.fa;
break;
}
case "Combine": {
const p: any = stack;
stack = S.stack(current.right, p);
current = current.left;
break;
}
}
if (current === undefined) {
if (stack !== undefined) {
current = stack.value;
stack = stack.previous;
}
}
}
return as;
};