crazy-parser
Version:
A light-weight parser combinator
107 lines (106 loc) • 3.85 kB
JavaScript
export const str = input => {
if (typeof input != "string")
return new TypeError(`Expected string, got ${JSON.stringify(input)}`);
return input;
};
export const num = (input => {
if (typeof input != "number")
return new TypeError(`Expected number, got ${JSON.stringify(input)}`);
return input;
});
export const bool = input => {
if (typeof input != "boolean")
return new TypeError(`Expected boolean, got ${JSON.stringify(input)}`);
return input;
};
export const nil = input => {
if (input !== null)
return new TypeError(`Expected null, got ${JSON.stringify(input)}`);
return null;
};
export const array = inner => input => {
if (!Array.isArray(input))
return new TypeError(`Expected array, got ${JSON.stringify(input)}`);
for (let i in input) {
const item = input[i];
const result = inner(item);
if (result instanceof TypeError)
return new TypeError(`Invalid index ${i}: ${result.message}`);
}
return structuredClone(input);
};
export const obj = (inner, fallback) => input => {
if (typeof input != "object" || input === null || Array.isArray(input))
return new TypeError(`Expected object, got ${JSON.stringify(input)}`);
let _input = input;
for (const key in inner) {
const validator = inner[key];
if (!(key in _input)) {
if (!fallback || !(key in fallback))
return new TypeError(`Missing key: ${key}`);
if (input == _input)
_input = structuredClone(_input);
_input[key] = fallback[key];
continue;
}
const result = validator(_input[key]);
if (result instanceof TypeError) {
if (!fallback || !(key in fallback))
return new TypeError(`Invalid key ${key}: ${result.message}`);
if (input == _input)
_input = structuredClone(_input);
_input[key] = fallback[key];
}
}
return _input;
};
export const sequence = (...inners) => input => {
if (!Array.isArray(input))
return new TypeError(`Expected tuple, got ${JSON.stringify(input)}`);
if (input.length != inners.length)
return new TypeError(`Expected tuple of length ${inners.length}, got ${JSON.stringify(input)}`);
for (const i in inners) {
const validator = inners[i];
const item = input[i];
const result = validator(item);
if (result instanceof TypeError)
return new TypeError(`Invalid index ${i}: ${result.message}`);
}
return input;
};
export const asum = (...inners) => input => {
const errors = [];
for (const validator of inners) {
const result = validator(input);
if (result instanceof TypeError)
errors.push(result);
else
return result;
}
return new TypeError(`No alternatives matched, got errors: ${errors.map(e => e.message).join("; ")}`);
};
export const ands = (...inners) => input => {
for (const i in inners) {
const validator = inners[i];
const result = validator(input);
if (result instanceof TypeError)
return new TypeError(`Failed at step ${i}: ${result.message}`);
}
return input;
};
export const eq = value => input => {
if (input !== value)
return new TypeError(`Expected ${JSON.stringify(value)}, got ${JSON.stringify(input)}`);
return value;
};
export const where = (validator, predicate, error) => input => {
const result = validator(input);
if (result instanceof TypeError)
return result;
if (!predicate(result))
return error ?? new TypeError(`Value did not satisfy predicate, got ${JSON.stringify(result)}`);
return result;
};
export function lazy(vg) {
return (input => vg()(input));
}