functionalscript
Version:
FunctionalScript is a purely functional subset of JavaScript
263 lines (262 loc) • 8.76 kB
JavaScript
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 { contains, set } from "../../types/string_set/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, name) => {
if (dr === null)
return null;
return { tag: dr.tag, rules: [...dr.rules, name] };
};
const addTagToDispatch = (dr, tag) => {
if (dr === null)
return null;
return { tag, rules: dr.rules };
};
const dispatchRule = (dm, name, current) => {
if (name in dm) {
return dm;
}
const newCurrent = set(name)(current);
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) {
if (contains(item)(newCurrent)) {
result = result.map(x => [addRuleToDispatch(x[0], item), x[1]]);
}
else {
dm = dispatchRule(dm, item, newCurrent);
const dr = dm[item];
if (emptyTag === true) {
result = result.map(x => [addRuleToDispatch(x[0], item), x[1]]);
result = toArray(dispatchOp.merge(result)(dr.rangeMap));
emptyTag = dr.emptyTag !== undefined ? true : undefined;
}
else {
result = result.map(x => [addRuleToDispatch(x[0], item), 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, newCurrent);
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, null);
}
return result;
};
const emptyTagMapAdd = (ruleSet) => (map) => (name) => {
if (name in map) {
return [ruleSet, map, map[name]];
}
const rule = ruleSet[name];
if (typeof rule === 'number') {
return [ruleSet, { ...map, [name]: false }, false];
}
else if (rule instanceof Array) {
map = { ...map, [name]: true };
let emptyTag = rule.length == 0;
for (const item of rule) {
const [, newMap, itemEmptyTag] = emptyTagMapAdd(ruleSet)(map)(item);
map = newMap;
if (emptyTag === false) {
emptyTag = itemEmptyTag !== false;
}
}
return [ruleSet, { ...map, [name]: emptyTag }, emptyTag];
}
else {
map = { ...map, [name]: true };
const entries = Object.entries(rule);
let emptyTag = false;
for (const [tag, item] of entries) {
const [, newMap, itemEmptyTag] = emptyTagMapAdd(ruleSet)(map)(item);
map = newMap;
if (itemEmptyTag !== false) {
emptyTag = tag;
}
}
return [ruleSet, { ...map, [name]: emptyTag }, emptyTag];
}
};
export const createEmptyTagMap = (data) => {
return emptyTagMapAdd(data[0])({})(data[1])[1];
};
// export const parserDescent = (fr: FRule): Match => {
// const data = toData(fr)
// const getEmptyTag = (rule: Rule): EmptyTag => {
// return todo()
// }
// const f: DescentMatchRule = (r, cp, idx): MatchResult => {
// const mrSuccess = (tag: AstTag, sequence: AstSequence, r: Remainder): MatchResult => [{tag, sequence}, true, r]
// const mrFail = (tag: AstTag, sequence: AstSequence, r: Remainder): MatchResult => [{tag, sequence}, false, r]
// if (idx >= cp.length) {
// const emptyTag = getEmptyTag(r)
// return mrSuccess(emptyTag, [], emptyTag === undefined ? null : cp)
// }
// return todo()
// }
// const match: Match = (name, cp): MatchResult => {
// return f(data[0][name], cp, 0)
// }
// return match
// }
export const parser = (fr) => {
const data = toData(fr);
return parserRuleSet(data[0]);
};
export const parserRuleSet = (ruleSet) => {
const map = dispatchMap(ruleSet);
const f = (rule, cp) => {
const mrSuccess = (tag, sequence, r) => [{ tag, sequence }, true, r];
const mrFail = (tag, sequence, r) => [{ tag, sequence }, false, r];
const { emptyTag, rangeMap } = rule;
if (cp.length === 0) {
return mrSuccess(emptyTag, [], emptyTag === undefined ? null : cp);
}
const cp0 = cp[0];
const dr = dispatchOp.get(cp0)(rangeMap);
if (dr === null) {
if (emptyTag === undefined) {
return mrFail(emptyTag, [], cp);
}
return mrSuccess(emptyTag, [], cp);
}
let seq = [cp0];
let r = cp;
const [_, ...restCp] = cp;
r = restCp;
const { tag, rules } = dr;
for (const i of rules) {
const rule = typeof i === 'string' ? map[i] : i;
const res = f(rule, r);
const [astRule, success, newR] = res;
if (success === false) {
return res;
}
seq = [...seq, astRule];
if (newR === null) {
return mrSuccess(tag, seq, null);
}
r = newR;
}
return mrSuccess(tag, seq, r);
};
return (name, cp) => f(map[name], cp);
};