UNPKG

functionalscript

Version:

FunctionalScript is a purely functional subset of JavaScript

263 lines (262 loc) 8.76 kB
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); };