UNPKG

typify

Version:

Runtime type-checking for JavaScript.

219 lines (199 loc) 4.82 kB
"use strict"; var utils = require("./utils.js"); // typify: instance Thunk // Thunk, for the lazy-evaluation and recursive parser. // :: fn -> undefined function Thunk(f) { this.thunk = f; this.forced = false; this.value = undefined; } // :: any -> Thunk function delay(f) { return new Thunk(f); } // :: any -> * function force(thunk) { if (thunk instanceof Thunk) { if (!thunk.forced) { thunk.value = thunk.thunk(); thunk.forced = true; } return thunk.value; } else { return thunk; } } // :: fn -> array string -> * function parse(p, tokens) { var res = p(tokens, 0); // console.log("parse", res, tokens, tokens.length); if (res !== undefined && res[1] >= tokens.length) { return res[0]; } else { return undefined; } } // :: array string -> nat -> (tuple undefined idx)? function eof(tokens, idx) { // console.log("eof", tokens, idx); if (idx < tokens.length) { return undefined; } else { return [undefined, idx]; } } // :: string -> fn function token(tok) { // :: array string -> nat -> (tuple string nat)? return function (tokens, idx) { // console.log("token", tokens, idx, tok); if (idx >= tokens.length) { return undefined; } if (tokens[idx] === tok) { return [tok, idx + 1]; } else { return undefined; } }; } // :: fn -> fn function satisfying(predicate) { // :: array string -> nat -> (tuple string nat)? return function (tokens, idx) { // console.log("satisfying", predicate.name || predicate, tokens, idx); if (idx >= tokens.length) { return undefined; } if (predicate(tokens[idx])) { return [tokens[idx], idx + 1]; } else { return undefined; } }; } // :: -> fn function any() { // :: array string -> nat -> (tuple string nat)? return function (tokens, idx) { if (idx < tokens.length) { return [tokens[idx], idx + 1]; } else { return undefined; } }; } // :: * -> fn function pure(x) { // :: array string -> nat -> tuple * nat return function (tokens, idx) { return [x, idx]; }; } // :: fn... -> fn function or() { var args = utils.slice(arguments); var len = args.length; // :: array string -> nat -> (tuple * nat)? return function (tokens, idx) { for (var i = 0; i < len; i++) { var res = force(args[i])(tokens, idx); if (res !== undefined) { return res; } } return undefined; }; } // :: fn | Thunk ... -> fn function lift() { var len = arguments.length - 1; var f = arguments[len]; var args = utils.slice(arguments, 0, -1); // :: array string -> nat -> (tuple * nat)? return function (tokens, idx) { var resargs = new Array(len); for (var i = 0; i < len; i++) { var res = force(args[i])(tokens, idx); // console.log("lift argument:", res, force(args[i])); if (res === undefined) { return undefined; } resargs[i] = res[0]; idx = res[1]; } // console.log("lift value", f.apply(undefined, resargs), idx); return [f.apply(undefined, resargs), idx]; }; } // :: array -> * -> array string -> nat -> tuple array nat function manyLoop(res, a, tokens, idx) { while (true) { var aq = a(tokens, idx); if (aq === undefined) { return [res, idx]; } res.push(aq[0]); idx = aq[1]; } } // :: fn -> fn function some(a) { // :: array string -> nat -> (tuple array nat)? return function (tokens, idx) { a = force(a); var res = []; var ap = a(tokens, idx); if (ap === undefined) { return undefined; } res.push(ap[0]); idx = ap[1]; return manyLoop(res, a, tokens, idx); }; } // :: fn -> fn function many(a) { // :: array string -> nat -> tuple array nat return function (tokens, idx) { a = force(a); var res = []; return manyLoop(res, a, tokens, idx); }; } // :: fn -> string -> fn function sepBy(a, sep) { // :: array string -> nat -> (tuple array nat)? return function (tokens, idx) { a = force(a); var res = []; var ap = a(tokens, idx); if (ap === undefined) { return undefined; } res.push(ap[0]); idx = ap[1]; while (true) { if (tokens[idx] !== sep) { return [res, idx]; } idx += 1; var aq = a(tokens, idx); if (aq === undefined) { return [res, idx]; } res.push(aq[0]); idx = aq[1]; } }; } // :: fn -> * -> fn function optional(p, def) { // :: array string -> nat -> (tuple * nat)? return function (tokens, idx) { var res = force(p)(tokens, idx); if (res === undefined) { return [def, idx]; } else { return res; } }; } module.exports = { parse: parse, pure: pure, or: or, lift: lift, many: many, some: some, sepBy: sepBy, eof: eof, token: token, any: any, satisfying: satisfying, optional: optional, delay: delay, };