UNPKG

typify

Version:

Runtime type-checking for JavaScript.

158 lines (142 loc) 5.99 kB
"use strict"; var utils = require("./utils.js"); var p = require("./predicates.js"); // :: Environment -> map (array checkable) -> array string -> checkableAlt | checkableAnd -> fn function compileAndAlt(environment, context, recNames, parsed, operator) { var cs = utils.map(parsed.options, compileCheckableTypeRecursive.bind(undefined, environment, context, recNames)); // compiledOptAlt :: map fn -> fn -> any -> fn... -> boolean operator = parsed.type === "and" ? "every" : "some"; return function (recChecks, varCheck, arg) { return cs[operator](function (c) { return c(recChecks, varCheck, arg); }); }; } // :: Environment -> map (array checkable) -> array string -> checkableVar -> fn function compileVar(environment, context, recNames, parsed) { if (utils.has(context, parsed.name)) { // compiledContext :: map fn -> fn -> any -> fn... -> boolean return function (recChecks, varCheck, arg) { // console.log("varcheck", varCheck, arg); return varCheck(parsed.name, arg); }; } else if (environment.has(parsed.name)) { var check = environment.get(parsed.name); // compiledEnv :: map fn -> fn -> any -> fn... -> boolean return function (recChecks, varCheck) { var args = utils.slice(arguments, 2); return check.apply(undefined, args); }; } else if (recNames && utils.contains(recNames, parsed.name)) { // compiledRec :: map fn -> fn -> any -> fn... -> boolean return function (recChecks, varCheck, arg) { return recChecks[parsed.name](recChecks, varCheck, arg); }; } else { throw new Error("unknown type: " + parsed.name); } } // :: Environment -> map (array checkable) -> array string -> checkablePoly -> fn function compilePoly(environment, context, recNames, parsed) { var args = utils.map(parsed.args, compileCheckableTypeRecursive.bind(undefined, environment, context, recNames)); if (utils.has(context, parsed.name)) { // compiledPoly :: map fn -> fn -> any -> fn... -> boolean return function compiledPolyEnv(recChecks, varCheck, arg) { var argsChecks = args.map(function (argCheck) { return argCheck.bind(undefined, recChecks, varCheck); }); return varCheck.apply(undefined, [parsed.name, arg].concat(argsChecks)); }; } else if (environment.has(parsed.name)) { var polyCheck = environment.get(parsed.name); // compiledPoly :: map fn -> fn -> any -> fn... -> boolean return function (recChecks, varCheck, arg) { var argsChecks = args.map(function (argCheck) { return argCheck.bind(undefined, recChecks, varCheck); }); return polyCheck.apply(undefined, [arg].concat(argsChecks)); }; } else { throw new Error("unknown type: " + parsed.name); } } // :: Environment -> map (array checkable) -> array string -> checkableOpt -> fn function compileOpt(environment, context, recNames, parsed) { var c = compileCheckableTypeRecursive(environment, context, recNames, parsed.term); // compiledOpt :: map fn -> fn -> any -> fn... -> boolean return function (recChecks, varCheck, arg) { return arg === undefined || c(recChecks, varCheck, arg); }; } // :: Environment -> map (array checkable) -> array string -> checkableLiteral -> fn function compileLiteral(environment, context, recNames, parsed) { if (parsed.value !== parsed.value) { // NaN // compiledNaN :: map fn -> fn -> any -> fn... -> boolean return function (recChecks, varCheck, arg) { return arg !== arg; }; } else { // compiledLiteral :: map fn -> fn -> any -> fn... -> boolean return function (recChecks, varCheck, arg) { return arg === parsed.value; }; } } // :: Environment -> map (array checkable) -> array string -> checkableRecord -> fn function compileRecord(environment, context, recNames, parsed) { var fields = {}; for (var name in parsed.fields) { fields[name] = compileCheckableTypeRecursive(environment, context, recNames, parsed.fields[name]); } var closed = parsed.closed; // compiledRecord : map fn -> fn -> any -> fn... -> boolean return function (recChecks, varCheck, arg) { if (!p.isObject(arg)) { return false; } for (var fieldName in fields) { if (!fields[fieldName](recChecks, varCheck, arg[fieldName])) { return false; } } if (closed) { for (var key in arg) { if (!fields[key]) { return false; } } } return true; }; } // :: Environment -> map (array checkable) -> array string -> checkableUser -> fn function compileUser(environment, context, recNames, parsed) { // compiledUser :: map fn -> fn -> any -> fn... -> boolean return function (recChecks, varCheck, arg) { return parsed.predicate(arg); }; } // :: Environment -> map (array checkable) -> array string -> checkable -> fn function compileCheckableTypeRecursive(environment, context, recNames, parsed) { switch (parsed.type) { case "var": return compileVar(environment, context, recNames, parsed); case "literal": return compileLiteral(environment, context, recNames, parsed); case "poly": return compilePoly(environment, context, recNames, parsed); case "any": return p.constTrue; case "opt": return compileOpt(environment, context, recNames, parsed); case "alt": return compileAndAlt(environment, context, recNames, parsed); case "and": return compileAndAlt(environment, context, recNames, parsed); case "record": return compileRecord(environment, context, recNames, parsed); case "user": return compileUser(environment, context, recNames, parsed); } } // :: Environment -> map (array checkable) -> checkable -> fn function compileCheckableType(environment, context, parsed) { return compileCheckableTypeRecursive(environment, context, [], parsed).bind(undefined, {}); } module.exports = { compile: compileCheckableType, compileRecursive: compileCheckableTypeRecursive, compileRecord: compileRecord, };