120 lines
3.5 KiB
JavaScript
120 lines
3.5 KiB
JavaScript
(() => {
|
|
|
|
/// Delimiter list -> String -> list of parts, delimiters, offsets
|
|
|
|
// multiSplit :: [String] -> String ->
|
|
// [{part::String, delim::String, offset::Int}]
|
|
const multiSplit = (ds, s) => {
|
|
const
|
|
dcs = map(chars, ds),
|
|
xs = chars(s),
|
|
dct = foldl(
|
|
(a, c, i, s) => {
|
|
const
|
|
inDelim = a.offset > i,
|
|
mb = inDelim ? (
|
|
nothing('')
|
|
) : find(d => isPrefixOf(d, drop(i, xs)), dcs);
|
|
return mb.nothing ? {
|
|
tokens: a.tokens.concat(inDelim ? (
|
|
[]
|
|
) : [c]),
|
|
parts: a.parts,
|
|
offset: a.offset
|
|
} : {
|
|
tokens: [],
|
|
parts: append(a.parts, [{
|
|
part: intercalate('', a.tokens),
|
|
delim: intercalate('', mb.just),
|
|
offset: i
|
|
}]),
|
|
offset: i + length(mb.just)
|
|
};
|
|
}, {
|
|
tokens: [],
|
|
parts: [],
|
|
offset: 0
|
|
}, xs
|
|
);
|
|
return append(dct.parts, [{
|
|
part: intercalate('', dct.tokens),
|
|
delim: "",
|
|
offset: length(s)
|
|
}]);
|
|
};
|
|
|
|
// GENERIC FUNCTIONS -----------------------------------------------------
|
|
|
|
// append (++) :: [a] -> [a] -> [a]
|
|
const append = (xs, ys) => xs.concat(ys);
|
|
|
|
// chars :: String -> [Char]
|
|
const chars = s => s.split('');
|
|
|
|
// drop :: Int -> [a] -> [a]
|
|
// drop :: Int -> String -> String
|
|
const drop = (n, xs) => xs.slice(n);
|
|
|
|
// find :: (a -> Bool) -> [a] -> Maybe a
|
|
const find = (p, xs) => {
|
|
for (var i = 0, lng = xs.length; i < lng; i++) {
|
|
var x = xs[i];
|
|
if (p(x)) return just(x);
|
|
}
|
|
return nothing('Not found');
|
|
};
|
|
|
|
// foldl :: (a -> b -> a) -> a -> [b] -> a
|
|
const foldl = (f, a, xs) => xs.reduce(f, a);
|
|
|
|
// intercalate :: String -> [String] -> String
|
|
const intercalate = (s, xs) => xs.join(s);
|
|
|
|
// isPrefixOf takes two lists or strings and returns
|
|
// true iff the first is a prefix of the second.
|
|
// isPrefixOf :: [a] -> [a] -> Bool
|
|
// isPrefixOf :: String -> String -> Bool
|
|
const isPrefixOf = (xs, ys) => {
|
|
const pfx = (xs, ys) => xs.length ? (
|
|
ys.length ? xs[0] === ys[0] && pfx(
|
|
xs.slice(1), ys.slice(1)
|
|
) : false
|
|
) : true;
|
|
return typeof xs !== 'string' ? pfx(xs, ys) : ys.startsWith(xs);
|
|
};
|
|
|
|
// just :: a -> Just a
|
|
const just = x => ({
|
|
nothing: false,
|
|
just: x
|
|
});
|
|
|
|
// length :: [a] -> Int
|
|
const length = xs => xs.length;
|
|
|
|
// map :: (a -> b) -> [a] -> [b]
|
|
const map = (f, xs) => xs.map(f);
|
|
|
|
// nothing :: () -> Nothing
|
|
const nothing = (optionalMsg) => ({
|
|
nothing: true,
|
|
msg: optionalMsg
|
|
});
|
|
|
|
// show :: Int -> a -> Indented String
|
|
// show :: a -> String
|
|
const show = (...x) =>
|
|
JSON.stringify.apply(
|
|
null, x.length > 1 ? [x[1], null, x[0]] : x
|
|
);
|
|
|
|
// TEST ------------------------------------------------------------------
|
|
const
|
|
strTest = 'a!===b=!=c',
|
|
delims = ['==', '!=', '='];
|
|
|
|
return show(2,
|
|
multiSplit(delims, strTest)
|
|
);
|
|
})();
|