@focuson/lens
Version:
A simple implementation of lens using type script
187 lines (186 loc) • 7.63 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.replaceTextUsingPathForS = exports.replaceTextFn = exports.processPath = exports.stateCodeBuilder = exports.stateCodeInitials = exports.lensBuilder = exports.prefixNameAndLens = exports.parsePath = exports.tokenisePath = void 0;
const index_1 = require("../index");
function makeError(s, msg) {
return Error(`${s.errorPrefix} ${msg}`);
}
function tokenisePath(p) {
var i = 0;
const tokens = [];
var accStart = 0;
function addAcc() {
let s = p.slice(accStart, i - 1);
if (inSpecialBrackets)
s = s.startsWith('$') ? s : '$' + s;
if (s !== '')
tokens.push(s);
accStart = i;
inSpecialBrackets = false;
}
var inSpecialBrackets = false;
while (true) {
if (i >= p.length) {
if (accStart < p.length)
tokens.push(p.slice(accStart));
return tokens;
}
const ch = p[i];
i = i + 1;
if (inSpecialBrackets && ch === ']')
addAcc();
else if (ch === '[' && p[i] && (p[i] === '$' || p[i].match(/[0-9]/))) {
addAcc();
inSpecialBrackets = true;
}
else if (ch === '[' || ch === ']' || ch === '~' || ch === '/') {
addAcc();
tokens.push(ch);
}
else if (!ch.match(/[0-9A-Za-z._$#]/))
throw new Error(`Illegal character [${ch}] at position ${i - 1} in [${p}]`);
}
}
exports.tokenisePath = tokenisePath;
function parsePath(path, p) {
const tokens = tokenisePath(path);
const build = undefined;
const s = { build, tokens: tokens.reverse(), errorPrefix: `Error parsing '${path}'.` };
processPath(s, p, false);
return s.build;
}
exports.parsePath = parsePath;
function prefixNameAndLens(...choices) {
const result = { '/': index_1.Lenses.identity() };
choices.forEach(([p, l]) => result[p] = l);
return result;
}
exports.prefixNameAndLens = prefixNameAndLens;
function lensBuilder(prefixs, variables) {
const id = index_1.Lenses.identity();
return {
initialVariable(name) { var _a; return (_a = variables[name]) === null || _a === void 0 ? void 0 : _a.call(variables, id); },
isVariable(name) { return variables[name] !== undefined; },
foldVariable(acc, name) { var _a; return acc.chain((_a = variables[name]) === null || _a === void 0 ? void 0 : _a.call(variables, id)); },
zero(initial) { return prefixs[initial]; },
foldBracketsPath(acc, path) { return acc.chainCalc(path); },
foldAppend(acc) { return acc.chain(index_1.Lenses.append()); },
foldKey(acc, key) { return acc.focusQuery(key); },
foldLast(acc) { return acc.chain(index_1.Lenses.last()); },
foldNth(acc, n) { return acc.chain(index_1.Lenses.nth(n)); }
};
}
exports.lensBuilder = lensBuilder;
function stateCodeInitials(stateName) {
return { '': 'state', '~': 'fullState', '/': `state.copyWithIdentity()` };
}
exports.stateCodeInitials = stateCodeInitials;
function stateCodeBuilder(initials, optionalsName, focusQuery) {
const realFocusQuery = focusQuery ? focusQuery : 'focusQuery';
return {
initialVariable(name) { return `${initials['']}.copyWithLens(${optionalsName}.${name}(identityL))`; },
isVariable(name) { return !name.match(/^[0-9]+$/); },
foldVariable(acc, name) { return `.chain(${optionalsName}.${name}(identityL))`; },
zero(initial) { return initials[initial]; },
foldBracketsPath(acc, path) { return acc + `.chainNthFromPath(${path})`; },
foldKey(acc, key) { return acc + `.${realFocusQuery}('${key}')`; },
foldAppend(acc) { return acc + ".chain(Lenses.append())"; },
foldLast(acc) { return acc + ".chain(Lenses.last())"; },
foldNth(acc, n) { return acc + `.chain(Lenses.nth(${n}))`; }
};
}
exports.stateCodeBuilder = stateCodeBuilder;
function processInitialToken(s, p) {
const initial = s.tokens[s.tokens.length - 1];
if (initial.startsWith('#')) {
s.build = p.initialVariable(initial.slice(1));
if (s.build === undefined)
throw makeError(s, `Cannot find variable ${initial}`);
s.tokens.pop();
return;
}
let hasSpecificInitial = initial === '/' || initial === '~';
const actualInitial = hasSpecificInitial ? initial : '';
if (hasSpecificInitial)
s.tokens.pop();
s.build = p.zero(actualInitial);
if (s.build === undefined)
throw makeError(s, `Cannot find initial '${actualInitial}'`);
}
function processPath(s, p, expectBracket) {
if (s.tokens.length == 0) {
s.build = p.zero('');
return;
}
processInitialToken(s, p);
while (true) {
const token = s.tokens.pop();
if (token === undefined)
if (expectBracket)
throw makeError(s, 'Ran out of tokens!');
else
return;
if (token === ']')
if (expectBracket)
return;
else
throw makeError(s, 'Unexpected ]');
if (token.startsWith('#')) {
let body = token.slice(1);
if (p.isVariable(body))
s.build = p.foldVariable(s.build, body);
else
throw makeError(s, `Variable ${token} not found`);
}
else if (token === '$append')
s.build = p.foldAppend(s.build);
else if (token === '$last')
s.build = p.foldLast(s.build);
else if (token.startsWith('$')) {
let body = token.slice(1);
let n = Number.parseInt(body);
if (n === Number.NaN)
throw makeError(s, `'${body} is not a number`);
s.build = p.foldNth(s.build, n);
}
else if (token === '/') { } //
else if (token === '~') {
throw makeError(s, 'Unexpected ~');
} //
else if (token === '[') {
const newState = Object.assign({}, s); //note: not copying tokens: this is mutable .. we still consume tokens inside this, but there is a new state.
processPath(newState, p, true); //the ] has been popped.
s.build = p.foldBracketsPath(s.build, newState.build);
}
else {
s.build = p.foldKey(s.build, token);
}
}
}
exports.processPath = processPath;
function replaceTextFn(errorPrefix, s, from, f) {
function escape(s) {
return s.replace(/</g, '<').replace(/>/g, '>');
}
const parts = f.slice(1, -1).split("|");
const value = from(parts[0]).getOption(s);
if (value === undefined)
return '';
if (parts.length == 1)
return escape(`${value}`);
if (typeof value == 'boolean') {
if (parts.length == 3)
return escape(value ? parts[2] : parts[1]);
throw new Error(`${errorPrefix} Replacing string ${f} and it's a boolean [${value}], but there are ${parts.length - 1} options, not 2`);
}
if (typeof value == 'number') {
if (value >= 0 && value <= parts.length)
return escape(parts[value + 1]);
throw new Error(`${errorPrefix} Replacing string ${f} and it's a string [${value}], but there are ${parts.length - 1} options, and this value is out of range`);
}
}
exports.replaceTextFn = replaceTextFn;
function replaceTextUsingPathForS(errorPrefix, s, fromPath, string) {
return string.replace(/{[^}]*}/g, str => replaceTextFn(errorPrefix, s, fromPath, str));
}
exports.replaceTextUsingPathForS = replaceTextUsingPathForS;
;