functionalscript
Version:
FunctionalScript is a purely functional subset of JavaScript
171 lines (168 loc) • 5.08 kB
JavaScript
import { todo } from "../../dev/module.f.js";
import { stringToCodePointList } from "../../text/utf16/module.f.js";
import { strictEqual } from "../../types/function/operator/module.f.js";
import { map, toArray } from "../../types/list/module.f.js";
import { rangeMap } from "../../types/range_map/module.f.js";
import { oneEncode, rangeDecode, } from "../module.f.js";
const { entries } = Object;
const find = (map) => (fr) => {
for (const [k, v] of entries(map)) {
if (v === fr) {
return k;
}
}
return undefined;
};
const newName = (map, name) => {
let i = 0;
let result = name;
while (result in map) {
result = name + i;
++i;
}
return result;
};
const sequence = (list) => map => {
let result = [];
let set = {};
for (const fr of list) {
const [map1, set1, id] = toDataAdd(map)(fr);
map = map1;
set = { ...set, ...set1 };
result = [...result, id];
}
return [map, set, result];
};
const variant = (fr) => map => {
let set = {};
let rule = {};
for (const [k, v] of entries(fr)) {
const [m1, s, id] = toDataAdd(map)(v);
map = m1;
set = { ...set, ...s };
rule = { ...rule, [k]: id };
}
return [map, set, rule];
};
const mapOneEncode = map(oneEncode);
const data = (dr) => {
switch (typeof dr) {
case 'string': {
return sequence(toArray(mapOneEncode(stringToCodePointList(dr))));
}
case 'number':
return m => [m, {}, dr];
default:
if (dr instanceof Array) {
return sequence(dr);
}
return variant(dr);
}
};
const toDataAdd = (map) => (fr) => {
{
const id = find(map)(fr);
if (id !== undefined) {
return [map, {}, id];
}
}
const [dr, tmpId] = typeof fr === 'function' ? [fr(), fr.name] : [fr, ''];
const newRule = data(dr);
const id = newName(map, tmpId);
const map1 = { ...map, [id]: fr };
const [map2, set, rule] = newRule(map1);
return [map2, { ...set, [id]: rule }, id];
};
export const toData = (fr) => {
const [, ruleSet, id] = toDataAdd({})(fr);
return [ruleSet, id];
};
const dispatchOp = rangeMap({
union: a => b => {
if (a === null) {
return b;
}
if (b === null) {
return a;
}
throw ['can not merge [', a, '][', b, ']'];
},
equal: strictEqual,
def: null,
});
export const dispatchMap = (ruleSet) => {
const addRuleToDispatch = (dr, rule) => {
if (dr === null)
return null;
return { tag: dr.tag, rules: [...dr.rules, rule] };
};
const addTagToDispatch = (dr, tag) => {
if (dr === null)
return null;
return { tag, rules: dr.rules };
};
const dispatchRule = (dm, name) => {
if (name in dm) {
return dm;
}
const rule = ruleSet[name];
if (typeof rule === 'number') {
const range = rangeDecode(rule);
const dispatch = dispatchOp.fromRange(range)({ tag: undefined, rules: [] });
const dr = { emptyTag: undefined, rangeMap: dispatch };
return { ...dm, [name]: dr };
}
else if (rule instanceof Array) {
let emptyTag = true;
let result = [];
for (const item of rule) {
dm = dispatchRule(dm, item);
const dr = dm[item];
if (emptyTag === true) {
result = toArray(dispatchOp.merge(result)(dr.rangeMap));
emptyTag = dr.emptyTag !== undefined ? true : undefined;
}
else {
result = result.map(x => [addRuleToDispatch(x[0], dr), x[1]]);
}
}
const dr = { emptyTag, rangeMap: result };
return { ...dm, [name]: dr };
}
else {
const entries = Object.entries(rule);
let result = [];
let emptyTag = undefined;
for (const [tag, item] of entries) {
dm = dispatchRule(dm, item);
const dr = dm[item];
if (dr.emptyTag !== undefined) {
emptyTag = tag;
}
else {
const d = dr.rangeMap.map(x => [addTagToDispatch(x[0], tag), x[1]]);
result = toArray(dispatchOp.merge(result)(d));
}
}
const dr = { emptyTag, rangeMap: result };
return { ...dm, [name]: dr };
}
};
let result = {};
for (const k in ruleSet) {
result = dispatchRule(result, k);
}
return result;
};
export const parser = (fr) => {
const data = toData(fr);
return todo();
};
/**
* Either `{ variantItem: id }` or `id`.
*/
/*
type DispatchRule = SingleProperty<> | string
type Dispatch = RangeMapArray<DispatchRule>
type DispatchMap = { readonly[id in string]: Dispatch }
*/