├── .gitignore ├── .travis.yml ├── test ├── index.ls ├── check.ls └── parse-type.ls ├── src ├── index.ls ├── check.ls └── parse-type.ls ├── preroll ├── lib ├── index.js ├── check.js └── parse-type.js ├── package.json.ls ├── package.json ├── Makefile ├── LICENSE ├── README.md └── browser └── type-check.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | node_modules 3 | coverage 4 | *.t 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | arch: 2 | - amd64 3 | - ppc64le 4 | language: node_js 5 | node_js: 6 | - 12 7 | -------------------------------------------------------------------------------- /test/index.ls: -------------------------------------------------------------------------------- 1 | type-check = require '..' 2 | {strict-equal: equal} = require 'assert' 3 | 4 | suite 'index' -> 5 | test 'version' -> 6 | equal type-check.VERSION, (require '../package.json').version 7 | -------------------------------------------------------------------------------- /src/index.ls: -------------------------------------------------------------------------------- 1 | VERSION = '0.4.0' 2 | parse-type = require './parse-type' 3 | parsed-type-check = require './check' 4 | 5 | type-check = (type, input, options) -> 6 | parsed-type-check (parse-type type), input, options 7 | 8 | module.exports = {VERSION, type-check, parsed-type-check, parse-type} 9 | -------------------------------------------------------------------------------- /preroll: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var version = require("./lib").VERSION; 4 | var lsVersion = require('livescript').VERSION; 5 | console.log("// Generated by LiveScript " + lsVersion 6 | + "\n" 7 | + "\n// type-check " + version 8 | + "\n// Copyright (c) George Zahariev" 9 | + "\n// Released under the MIT License" 10 | + "\n// https://raw.githubusercontent.com/gkz/type-check/master/LICENSE"); 11 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | // Generated by LiveScript 1.6.0 2 | (function(){ 3 | var VERSION, parseType, parsedTypeCheck, typeCheck; 4 | VERSION = '0.4.0'; 5 | parseType = require('./parse-type'); 6 | parsedTypeCheck = require('./check'); 7 | typeCheck = function(type, input, options){ 8 | return parsedTypeCheck(parseType(type), input, options); 9 | }; 10 | module.exports = { 11 | VERSION: VERSION, 12 | typeCheck: typeCheck, 13 | parsedTypeCheck: parsedTypeCheck, 14 | parseType: parseType 15 | }; 16 | }).call(this); 17 | -------------------------------------------------------------------------------- /package.json.ls: -------------------------------------------------------------------------------- 1 | name: 'type-check' 2 | version: '0.4.0' 3 | 4 | author: 'George Zahariev ' 5 | description: 'type-check allows you to check the types of JavaScript values at runtime with a Haskell like type syntax.' 6 | homepage: 'https://github.com/gkz/type-check' 7 | keywords: 8 | 'type' 9 | 'check' 10 | 'checking' 11 | 'library' 12 | files: 13 | 'lib' 14 | 'README.md' 15 | 'LICENSE' 16 | main: './lib/' 17 | 18 | bugs: 'https://github.com/gkz/type-check/issues' 19 | license: 'MIT' 20 | engines: 21 | node: '>= 0.8.0' 22 | repository: 23 | type: 'git' 24 | url: 'git://github.com/gkz/type-check.git' 25 | scripts: 26 | test: "make test" 27 | 28 | dependencies: 29 | 'prelude-ls': '^1.2.1' 30 | 31 | dev-dependencies: 32 | livescript: '^1.6.0' 33 | mocha: '^7.1.1' 34 | browserify: '^16.5.1' 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "type-check", 3 | "version": "0.4.0", 4 | "author": "George Zahariev ", 5 | "description": "type-check allows you to check the types of JavaScript values at runtime with a Haskell like type syntax.", 6 | "homepage": "https://github.com/gkz/type-check", 7 | "keywords": [ 8 | "type", 9 | "check", 10 | "checking", 11 | "library" 12 | ], 13 | "files": [ 14 | "lib", 15 | "README.md", 16 | "LICENSE" 17 | ], 18 | "main": "./lib/", 19 | "bugs": "https://github.com/gkz/type-check/issues", 20 | "license": "MIT", 21 | "engines": { 22 | "node": ">= 0.8.0" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "git://github.com/gkz/type-check.git" 27 | }, 28 | "scripts": { 29 | "test": "make test" 30 | }, 31 | "dependencies": { 32 | "prelude-ls": "^1.2.1" 33 | }, 34 | "devDependencies": { 35 | "livescript": "^1.6.0", 36 | "mocha": "^10.2.0", 37 | "browserify": "^16.5.1" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | default: all 2 | 3 | SRC = $(shell find src -name "*.ls" -type f | sort) 4 | LIB = $(SRC:src/%.ls=lib/%.js) 5 | 6 | LS = node_modules/livescript 7 | LSC = node_modules/.bin/lsc 8 | BROWSERIFY = node_modules/.bin/browserify 9 | MOCHA = node_modules/.bin/mocha 10 | 11 | package.json: package.json.ls 12 | $(LSC) --compile package.json.ls 13 | 14 | lib: 15 | mkdir -p lib/ 16 | 17 | lib/%.js: src/%.ls lib 18 | $(LSC) --compile --output lib "$<" 19 | 20 | browser: 21 | mkdir -p browser/ 22 | 23 | browser/type-check.js: $(LIB) browser 24 | { ./preroll ; $(BROWSERIFY) -r ./lib/index.js:type-check ; } > browser/type-check.js 25 | 26 | .PHONY: build build-browser test coverage dev-install loc clean 27 | 28 | all: build 29 | 30 | build: $(LIB) package.json 31 | 32 | build-browser: browser/type-check.js 33 | 34 | test: build 35 | $(MOCHA) --ui tdd --require livescript "test/**/*.ls" 36 | 37 | dev-install: package.json 38 | npm install . 39 | 40 | loc: 41 | wc -l $(SRC) 42 | 43 | clean: 44 | rm -f package.json 45 | rm -rf lib 46 | rm -rf browser 47 | rm -rf coverage 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) George Zahariev 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/check.ls: -------------------------------------------------------------------------------- 1 | {any, all, is-it-NaN} = require 'prelude-ls' 2 | 3 | types = 4 | Number: 5 | type-of: 'Number' 6 | validate: -> not is-it-NaN it 7 | NaN: 8 | type-of: 'Number' 9 | validate: is-it-NaN 10 | Int: 11 | type-of: 'Number' 12 | validate: -> not is-it-NaN it and it % 1 is 0 # 1.0 is an Int 13 | Float: 14 | type-of: 'Number' 15 | validate: -> not is-it-NaN it # same as number 16 | Date: 17 | type-of: 'Date' 18 | validate: -> not is-it-NaN it.get-time! # make sure it isn't an invalid date 19 | 20 | default-type = 21 | array: 'Array' 22 | tuple: 'Array' 23 | 24 | function check-array input, type, options 25 | all (-> check-multiple it, type.of, options), input 26 | 27 | function check-tuple input, type, options 28 | i = 0 29 | for types in type.of 30 | return false unless check-multiple input[i], types, options 31 | i++ 32 | input.length <= i # may be less if using 'Undefined' or 'Maybe' at the end 33 | 34 | function check-fields input, type, options 35 | input-keys = {} 36 | num-input-keys = 0 37 | for k of input 38 | input-keys[k] = true 39 | num-input-keys++ 40 | num-of-keys = 0 41 | for key, types of type.of 42 | return false unless check-multiple input[key], types, options 43 | num-of-keys++ if input-keys[key] 44 | type.subset or num-input-keys is num-of-keys 45 | 46 | function check-structure input, type, options 47 | return false if input not instanceof Object 48 | switch type.structure 49 | | 'fields' => check-fields input, type, options 50 | | 'array' => check-array input, type, options 51 | | 'tuple' => check-tuple input, type, options 52 | 53 | function check input, type-obj, options 54 | {type, structure} = type-obj 55 | if type 56 | return true if type is '*' # wildcard 57 | setting = options.custom-types[type] or types[type] 58 | if setting 59 | (setting.type-of is void or setting.type-of is typeof! input) 60 | and setting.validate input 61 | else 62 | # Booleam, String, Null, Undefined, Error, user defined objects, etc. 63 | type is typeof! input and (not structure or check-structure input, type-obj, options) 64 | else if structure 65 | return false unless that is typeof! input if default-type[structure] 66 | check-structure input, type-obj, options 67 | else 68 | throw new Error "No type defined. Input: #input." 69 | 70 | function check-multiple input, types, options 71 | throw new Error "Types must be in an array. Input: #input." unless typeof! types is 'Array' 72 | any (-> check input, it, options), types 73 | 74 | module.exports = (parsed-type, input, options = {}) -> 75 | options.custom-types = {} unless options.custom-types? 76 | check-multiple input, parsed-type, options 77 | -------------------------------------------------------------------------------- /lib/check.js: -------------------------------------------------------------------------------- 1 | // Generated by LiveScript 1.6.0 2 | (function(){ 3 | var ref$, any, all, isItNaN, types, defaultType, toString$ = {}.toString; 4 | ref$ = require('prelude-ls'), any = ref$.any, all = ref$.all, isItNaN = ref$.isItNaN; 5 | types = { 6 | Number: { 7 | typeOf: 'Number', 8 | validate: function(it){ 9 | return !isItNaN(it); 10 | } 11 | }, 12 | NaN: { 13 | typeOf: 'Number', 14 | validate: isItNaN 15 | }, 16 | Int: { 17 | typeOf: 'Number', 18 | validate: function(it){ 19 | return !isItNaN(it) && it % 1 === 0; 20 | } 21 | }, 22 | Float: { 23 | typeOf: 'Number', 24 | validate: function(it){ 25 | return !isItNaN(it); 26 | } 27 | }, 28 | Date: { 29 | typeOf: 'Date', 30 | validate: function(it){ 31 | return !isItNaN(it.getTime()); 32 | } 33 | } 34 | }; 35 | defaultType = { 36 | array: 'Array', 37 | tuple: 'Array' 38 | }; 39 | function checkArray(input, type, options){ 40 | return all(function(it){ 41 | return checkMultiple(it, type.of, options); 42 | }, input); 43 | } 44 | function checkTuple(input, type, options){ 45 | var i, i$, ref$, len$, types; 46 | i = 0; 47 | for (i$ = 0, len$ = (ref$ = type.of).length; i$ < len$; ++i$) { 48 | types = ref$[i$]; 49 | if (!checkMultiple(input[i], types, options)) { 50 | return false; 51 | } 52 | i++; 53 | } 54 | return input.length <= i; 55 | } 56 | function checkFields(input, type, options){ 57 | var inputKeys, numInputKeys, k, numOfKeys, key, ref$, types; 58 | inputKeys = {}; 59 | numInputKeys = 0; 60 | for (k in input) { 61 | inputKeys[k] = true; 62 | numInputKeys++; 63 | } 64 | numOfKeys = 0; 65 | for (key in ref$ = type.of) { 66 | types = ref$[key]; 67 | if (!checkMultiple(input[key], types, options)) { 68 | return false; 69 | } 70 | if (inputKeys[key]) { 71 | numOfKeys++; 72 | } 73 | } 74 | return type.subset || numInputKeys === numOfKeys; 75 | } 76 | function checkStructure(input, type, options){ 77 | if (!(input instanceof Object)) { 78 | return false; 79 | } 80 | switch (type.structure) { 81 | case 'fields': 82 | return checkFields(input, type, options); 83 | case 'array': 84 | return checkArray(input, type, options); 85 | case 'tuple': 86 | return checkTuple(input, type, options); 87 | } 88 | } 89 | function check(input, typeObj, options){ 90 | var type, structure, setting, that; 91 | type = typeObj.type, structure = typeObj.structure; 92 | if (type) { 93 | if (type === '*') { 94 | return true; 95 | } 96 | setting = options.customTypes[type] || types[type]; 97 | if (setting) { 98 | return (setting.typeOf === void 8 || setting.typeOf === toString$.call(input).slice(8, -1)) && setting.validate(input); 99 | } else { 100 | return type === toString$.call(input).slice(8, -1) && (!structure || checkStructure(input, typeObj, options)); 101 | } 102 | } else if (structure) { 103 | if (that = defaultType[structure]) { 104 | if (that !== toString$.call(input).slice(8, -1)) { 105 | return false; 106 | } 107 | } 108 | return checkStructure(input, typeObj, options); 109 | } else { 110 | throw new Error("No type defined. Input: " + input + "."); 111 | } 112 | } 113 | function checkMultiple(input, types, options){ 114 | if (toString$.call(types).slice(8, -1) !== 'Array') { 115 | throw new Error("Types must be in an array. Input: " + input + "."); 116 | } 117 | return any(function(it){ 118 | return check(input, it, options); 119 | }, types); 120 | } 121 | module.exports = function(parsedType, input, options){ 122 | options == null && (options = {}); 123 | if (options.customTypes == null) { 124 | options.customTypes = {}; 125 | } 126 | return checkMultiple(input, parsedType, options); 127 | }; 128 | }).call(this); 129 | -------------------------------------------------------------------------------- /src/parse-type.ls: -------------------------------------------------------------------------------- 1 | # helpers 2 | identifier-regex = /[\$\w]+/ 3 | 4 | function peek tokens # use instead of 'tokens.0' when it is required that the next token exists 5 | token = tokens.0 6 | throw new Error 'Unexpected end of input.' unless token? 7 | token 8 | 9 | function consume-ident tokens 10 | token = peek tokens 11 | throw new Error "Expected text, got '#token' instead." unless identifier-regex.test token 12 | tokens.shift! 13 | 14 | function consume-op tokens, op 15 | token = peek tokens 16 | throw new Error "Expected '#op', got '#token' instead." unless token is op 17 | tokens.shift! 18 | 19 | function maybe-consume-op tokens, op 20 | token = tokens.0 21 | if token is op then tokens.shift! else null 22 | 23 | # structures 24 | function consume-array tokens 25 | consume-op tokens, '[' 26 | throw new Error "Must specify type of Array - eg. [Type], got [] instead." if (peek tokens) is ']' 27 | types = consume-types tokens 28 | consume-op tokens, ']' 29 | {structure: 'array', of: types} 30 | 31 | function consume-tuple tokens 32 | components = [] 33 | consume-op tokens, '(' 34 | throw new Error "Tuple must be of at least length 1 - eg. (Type), got () instead." if (peek tokens) is ')' 35 | for ever 36 | components.push consume-types tokens 37 | maybe-consume-op tokens, ',' 38 | break if ')' is peek tokens 39 | consume-op tokens, ')' 40 | {structure: 'tuple', of: components} 41 | 42 | function consume-fields tokens 43 | fields = {} 44 | consume-op tokens, '{' 45 | subset = false 46 | for ever 47 | if maybe-consume-op tokens, '...' 48 | subset := true 49 | break 50 | [key, types] = consume-field tokens 51 | fields[key] = types 52 | maybe-consume-op tokens, ',' 53 | break if '}' is peek tokens 54 | consume-op tokens, '}' 55 | {structure: 'fields', of: fields, subset} 56 | 57 | function consume-field tokens 58 | key = consume-ident tokens 59 | consume-op tokens, ':' 60 | types = consume-types tokens 61 | [key, types] 62 | 63 | # core 64 | function maybe-consume-structure tokens 65 | switch tokens.0 66 | | '[' => consume-array tokens 67 | | '(' => consume-tuple tokens 68 | | '{' => consume-fields tokens 69 | 70 | function consume-type tokens 71 | token = peek tokens 72 | wildcard = token is '*' 73 | if wildcard or identifier-regex.test token 74 | type = if wildcard then consume-op tokens, '*' else consume-ident tokens 75 | structure = maybe-consume-structure tokens 76 | if structure then structure <<< {type} else {type} 77 | else 78 | structure = maybe-consume-structure tokens 79 | throw new Error "Unexpected character: #token" unless structure 80 | structure 81 | 82 | function consume-types tokens 83 | if '::' is peek tokens 84 | throw new Error "No comment before comment separator '::' found." 85 | lookahead = tokens.1 86 | if lookahead? and lookahead is '::' 87 | tokens.shift! # remove comment 88 | tokens.shift! # remove :: 89 | types = [] 90 | types-so-far = {} # for unique check 91 | if 'Maybe' is peek tokens 92 | tokens.shift! 93 | types = 94 | * type: 'Undefined' 95 | * type: 'Null' 96 | types-so-far = {+Undefined, +Null} 97 | for ever 98 | {type, structure}:type-obj = consume-type tokens 99 | types.push type-obj unless types-so-far[type] 100 | types-so-far[type] = true unless structure? 101 | break unless maybe-consume-op tokens, '|' 102 | types 103 | 104 | # single char ops used : , [ ] ( ) } { | * 105 | token-regex = // 106 | \.\.\. # etc op 107 | | :: # comment separator 108 | | -> # arrow (for error generation purposes) 109 | | #{ identifier-regex.source } # identifier 110 | | \S # all single char ops - valid, and non-valid (for error purposes) 111 | //g 112 | 113 | module.exports = (input) -> 114 | throw new Error 'No type specified.' unless input.length 115 | tokens = (input.match token-regex or []) 116 | if '->' in tokens 117 | throw new Error "Function types are not supported. 118 | \ To validate that something is a function, you may use 'Function'." 119 | try 120 | consume-types tokens 121 | catch 122 | throw new Error "#{e.message} - Remaining tokens: #{ JSON.stringify tokens } - Initial input: '#input'" 123 | -------------------------------------------------------------------------------- /lib/parse-type.js: -------------------------------------------------------------------------------- 1 | // Generated by LiveScript 1.6.0 2 | (function(){ 3 | var identifierRegex, tokenRegex; 4 | identifierRegex = /[\$\w]+/; 5 | function peek(tokens){ 6 | var token; 7 | token = tokens[0]; 8 | if (token == null) { 9 | throw new Error('Unexpected end of input.'); 10 | } 11 | return token; 12 | } 13 | function consumeIdent(tokens){ 14 | var token; 15 | token = peek(tokens); 16 | if (!identifierRegex.test(token)) { 17 | throw new Error("Expected text, got '" + token + "' instead."); 18 | } 19 | return tokens.shift(); 20 | } 21 | function consumeOp(tokens, op){ 22 | var token; 23 | token = peek(tokens); 24 | if (token !== op) { 25 | throw new Error("Expected '" + op + "', got '" + token + "' instead."); 26 | } 27 | return tokens.shift(); 28 | } 29 | function maybeConsumeOp(tokens, op){ 30 | var token; 31 | token = tokens[0]; 32 | if (token === op) { 33 | return tokens.shift(); 34 | } else { 35 | return null; 36 | } 37 | } 38 | function consumeArray(tokens){ 39 | var types; 40 | consumeOp(tokens, '['); 41 | if (peek(tokens) === ']') { 42 | throw new Error("Must specify type of Array - eg. [Type], got [] instead."); 43 | } 44 | types = consumeTypes(tokens); 45 | consumeOp(tokens, ']'); 46 | return { 47 | structure: 'array', 48 | of: types 49 | }; 50 | } 51 | function consumeTuple(tokens){ 52 | var components; 53 | components = []; 54 | consumeOp(tokens, '('); 55 | if (peek(tokens) === ')') { 56 | throw new Error("Tuple must be of at least length 1 - eg. (Type), got () instead."); 57 | } 58 | for (;;) { 59 | components.push(consumeTypes(tokens)); 60 | maybeConsumeOp(tokens, ','); 61 | if (')' === peek(tokens)) { 62 | break; 63 | } 64 | } 65 | consumeOp(tokens, ')'); 66 | return { 67 | structure: 'tuple', 68 | of: components 69 | }; 70 | } 71 | function consumeFields(tokens){ 72 | var fields, subset, ref$, key, types; 73 | fields = {}; 74 | consumeOp(tokens, '{'); 75 | subset = false; 76 | for (;;) { 77 | if (maybeConsumeOp(tokens, '...')) { 78 | subset = true; 79 | break; 80 | } 81 | ref$ = consumeField(tokens), key = ref$[0], types = ref$[1]; 82 | fields[key] = types; 83 | maybeConsumeOp(tokens, ','); 84 | if ('}' === peek(tokens)) { 85 | break; 86 | } 87 | } 88 | consumeOp(tokens, '}'); 89 | return { 90 | structure: 'fields', 91 | of: fields, 92 | subset: subset 93 | }; 94 | } 95 | function consumeField(tokens){ 96 | var key, types; 97 | key = consumeIdent(tokens); 98 | consumeOp(tokens, ':'); 99 | types = consumeTypes(tokens); 100 | return [key, types]; 101 | } 102 | function maybeConsumeStructure(tokens){ 103 | switch (tokens[0]) { 104 | case '[': 105 | return consumeArray(tokens); 106 | case '(': 107 | return consumeTuple(tokens); 108 | case '{': 109 | return consumeFields(tokens); 110 | } 111 | } 112 | function consumeType(tokens){ 113 | var token, wildcard, type, structure; 114 | token = peek(tokens); 115 | wildcard = token === '*'; 116 | if (wildcard || identifierRegex.test(token)) { 117 | type = wildcard 118 | ? consumeOp(tokens, '*') 119 | : consumeIdent(tokens); 120 | structure = maybeConsumeStructure(tokens); 121 | if (structure) { 122 | return structure.type = type, structure; 123 | } else { 124 | return { 125 | type: type 126 | }; 127 | } 128 | } else { 129 | structure = maybeConsumeStructure(tokens); 130 | if (!structure) { 131 | throw new Error("Unexpected character: " + token); 132 | } 133 | return structure; 134 | } 135 | } 136 | function consumeTypes(tokens){ 137 | var lookahead, types, typesSoFar, typeObj, type, structure; 138 | if ('::' === peek(tokens)) { 139 | throw new Error("No comment before comment separator '::' found."); 140 | } 141 | lookahead = tokens[1]; 142 | if (lookahead != null && lookahead === '::') { 143 | tokens.shift(); 144 | tokens.shift(); 145 | } 146 | types = []; 147 | typesSoFar = {}; 148 | if ('Maybe' === peek(tokens)) { 149 | tokens.shift(); 150 | types = [ 151 | { 152 | type: 'Undefined' 153 | }, { 154 | type: 'Null' 155 | } 156 | ]; 157 | typesSoFar = { 158 | Undefined: true, 159 | Null: true 160 | }; 161 | } 162 | for (;;) { 163 | typeObj = consumeType(tokens), type = typeObj.type, structure = typeObj.structure; 164 | if (!typesSoFar[type]) { 165 | types.push(typeObj); 166 | } 167 | if (structure == null) { 168 | typesSoFar[type] = true; 169 | } 170 | if (!maybeConsumeOp(tokens, '|')) { 171 | break; 172 | } 173 | } 174 | return types; 175 | } 176 | tokenRegex = RegExp('\\.\\.\\.|::|->|' + identifierRegex.source + '|\\S', 'g'); 177 | module.exports = function(input){ 178 | var tokens, e; 179 | if (!input.length) { 180 | throw new Error('No type specified.'); 181 | } 182 | tokens = input.match(tokenRegex) || []; 183 | if (in$('->', tokens)) { 184 | throw new Error("Function types are not supported.\ To validate that something is a function, you may use 'Function'."); 185 | } 186 | try { 187 | return consumeTypes(tokens); 188 | } catch (e$) { 189 | e = e$; 190 | throw new Error(e.message + " - Remaining tokens: " + JSON.stringify(tokens) + " - Initial input: '" + input + "'"); 191 | } 192 | }; 193 | function in$(x, xs){ 194 | var i = -1, l = xs.length >>> 0; 195 | while (++i < l) if (x === xs[i]) return true; 196 | return false; 197 | } 198 | }).call(this); 199 | -------------------------------------------------------------------------------- /test/check.ls: -------------------------------------------------------------------------------- 1 | {throws}:assert = require 'assert' 2 | {type-check: c, parsed-type-check} = require '..' 3 | 4 | suite 'check' -> 5 | test 'Undefined' -> 6 | assert c 'Undefined', void 7 | assert c 'Undefined', Math.FAKE 8 | assert not c 'Undefined', null 9 | assert not c 'Undefined', false 10 | 11 | test 'Undefined in field' -> 12 | assert c '{a: Undefined}', {} 13 | assert c '{a: Undefined}', {a: void} 14 | assert not c '{a: Undefined}', {a: 1} 15 | 16 | test 'Undefined in tuple' -> 17 | assert c '(Undefined, Number)', [void, 2] 18 | assert not c '(Undefined, Number)', [1, 2] 19 | 20 | test 'Null' -> 21 | assert c 'Null', null 22 | assert not c 'Null', void 23 | assert not c 'Null', false 24 | 25 | test 'Boolean' -> 26 | assert c 'Boolean', true 27 | assert c 'Boolean', false 28 | assert c 'Boolean', new Boolean false 29 | assert not c 'Boolean', 1 30 | 31 | test 'String' -> 32 | assert c 'String', 'hi' 33 | assert c 'String', new String 'hi' 34 | assert not c 'String', 2 35 | 36 | test 'Number' -> 37 | assert c 'Number', 2 38 | assert c 'Number', new Number 2 39 | assert not c 'Number', 'hi' 40 | 41 | test 'NaN' -> 42 | assert c 'NaN', NaN 43 | assert not c 'NaN', 1 44 | 45 | test 'Int' -> 46 | assert c 'Int', 1 47 | assert c 'Int', 1.0 48 | assert not c 'Int', 1.1 49 | 50 | test 'Float' -> 51 | assert c 'Float', 1 52 | assert c 'Float', 1.0 53 | assert c 'Float', 1.1 54 | 55 | test 'Date' -> 56 | assert c 'Date', new Date '2011-11-11' 57 | assert not c 'Date', new Date '2011-1111' 58 | 59 | test 'Function' -> 60 | assert c 'Function', -> 61 | 62 | test 'wildcard' -> 63 | assert c '*', void 64 | assert c '*', null 65 | assert c '*', 2 66 | assert c '*', {} 67 | assert c '*', new Error 68 | assert c '[*]', [1, null, void, 'hi', {x: 22}] 69 | 70 | test 'multiple' -> 71 | assert c 'Number | String', 'hi' 72 | assert not c 'Date | Number', 'hi' 73 | assert c 'String | [String] | [Object]', [{}] 74 | 75 | suite 'Array' -> 76 | test 'bare' -> 77 | assert c 'Array', [1, 2, 3] 78 | assert c 'Array', [1, 'hi'] 79 | assert not c 'Array', true 80 | 81 | test 'simple' -> 82 | assert c '[Number]', [1, 2, 3] 83 | 84 | test 'incorrect type' -> 85 | assert not c '[Number]', true 86 | 87 | test 'incorrect element type' -> 88 | assert not c '[Number]', [1, 'hi'] 89 | 90 | suite 'Tuple' -> 91 | test 'simple' -> 92 | assert c '(String, Number)', ['hi', 2] 93 | 94 | test 'too long' -> 95 | assert not c '(String, Number)', ['hi', 2, 1] 96 | 97 | test 'too short' -> 98 | assert not c '(String, Number)', ['hi'] 99 | 100 | test 'incorrect type' -> 101 | assert not c '(String, Number)', {} 102 | 103 | test 'incorrect element type' -> 104 | assert not c '(String, Number)', ['hi', 'bye'] 105 | 106 | test 'bare Object' -> 107 | assert c 'Object', {} 108 | assert c 'Object', {a: 1, length: 1} 109 | assert not c 'Object', new Date 110 | 111 | suite 'Maybe' -> 112 | test 'simple' -> 113 | assert c 'Maybe Number', 1 114 | assert c 'Maybe Number', null 115 | assert not c 'Maybe Number', 'string' 116 | 117 | test 'with multiple' -> 118 | type = 'Maybe Number | String' 119 | assert c type, 2 120 | assert c type, null 121 | assert c type, 'hi' 122 | 123 | test 'in fields' -> 124 | type = '{a: Maybe String}' 125 | assert c type, {a: 'string'} 126 | assert c type, {a: null} 127 | assert c type, {} 128 | assert not c type, {a: 2} 129 | 130 | test 'in tuple' -> 131 | type = '(Number, Maybe String)' 132 | assert c type, [1, 'hi'] 133 | assert c type, [1, null] 134 | assert c type, [1] 135 | assert not c '(Maybe String, Number)', [2] 136 | 137 | test 'in array' -> 138 | assert c '[Maybe Number]', [1, null, 2, void, 23] 139 | assert c 'Object[Maybe String]', {0: 'a', 2: null, 5: 'b', length: 6} 140 | 141 | suite 'duck typing' -> 142 | test 'basic' -> 143 | assert c '{a: String}', {a: 'hi'} 144 | 145 | test 'property must by appropriate type' -> 146 | assert not c '{a: String}', {a: 2} 147 | 148 | test 'key must be the same' -> 149 | assert not c '{a: String}', {b: 'hi'} 150 | 151 | test 'not an object - fails' -> 152 | assert not c '{a: String}', 2 153 | 154 | test 'non-enumerable properties' -> 155 | assert c '{parse: Function, stringify: Function}', JSON 156 | 157 | test 'enumerable and non-enumerable properties' -> 158 | assert c '{0: Number, 1: Number, length: Number}', [1, 2] 159 | 160 | test 'using spread operator to check only a subset of the properties' -> 161 | assert c '{length: Number, ...}', [1, 2] 162 | 163 | suite 'structures with types' -> 164 | test 'fields with Object' -> 165 | assert c 'Object{a: String}', {a: 'hi'} 166 | assert not c 'Object{a: String}', {a: 2} 167 | 168 | test 'fields with Array' -> 169 | assert c 'Array{0:Number, 1:Number, 2:Number}', [1, 2, 3] 170 | assert c 'Array{0:Number, 1:Number, 2:Number}', [1, 2, 3] 171 | assert c 'Array{0:Number, 1:String}', [1, 'hi'] 172 | assert not c 'Array{0:Number, 1:String}', [1] 173 | 174 | test 'fields with JSON' -> 175 | assert c 'JSON{parse: Function, stringify: Function}', JSON 176 | assert not c 'JSON{parse: Function, stringify: Function}', {parse: ->, stringify: ->} 177 | 178 | test 'fields with Math (using subset)' -> 179 | assert c 'Math{PI: Float, sqrt: Function, ...}', Math 180 | 181 | test 'array structure with Array' -> 182 | assert c 'Array[Number]', [1, 2] 183 | 184 | test 'array structure with Object' -> 185 | assert c 'Object[Number]', {0: 1, 1: 2, length: 2} 186 | 187 | test 'tuple structure with Array' -> 188 | assert c 'Array(Number, String)', [1, 'two'] 189 | 190 | test 'tuple structure with Object' -> 191 | assert c 'Object(Number, String)', {0: 1, 1: 'two', length: 2} 192 | 193 | suite 'custom types' -> 194 | test 'simple' -> 195 | o = 196 | custom-types: 197 | Even: 198 | type-of: 'Number' 199 | validate: -> it % 2 is 0 200 | assert c 'Even', 2, o 201 | assert not c 'Even', '2', o 202 | assert not c 'Even', 1, o 203 | 204 | test 'overwrite current' -> 205 | o = 206 | custom-types: 207 | Undefined: 208 | type-of: 'String' 209 | validate: -> it is 'bananas' 210 | 211 | assert c 'Undefined', 'bananas', o 212 | assert not c 'Undefined', void, o 213 | 214 | test 'type-of is optional' -> 215 | o = 216 | custom-types: 217 | str: 218 | validate: -> typeof it is 'string' 219 | 220 | assert c 'str', 'foo', o 221 | assert not c 'str', 1, o 222 | 223 | test 'nested check with custom types' -> 224 | o = 225 | custom-types: 226 | A: 227 | type-of: 'Object' 228 | validate: -> c 'String', it.foo and it.foo = 'A' 229 | B: 230 | type-of: 'Object' 231 | validate: -> c 'String', it.bar and it.bar = 'B' 232 | 233 | assert c '{a: A, b: B}', {a: {foo: 'A'}, b: {bar: 'B'}}, o 234 | 235 | test 'nested' -> 236 | type = '{a: (String, [Number], {x: {a: Maybe Number}, y: Array, ...}), b: Error{message: String, ...}}' 237 | assert c type, {a: ['hi', [1, 2, 3], {x: {a: 42}, y: [1, 'bye']}], b: new Error 'message'} 238 | assert c type, {a: ['moo', [3], {x: {}, y: [], z: 999}], b: new Error '23'} 239 | 240 | test 'nexted with union' -> 241 | assert c '[[Number] | [String]]', [[1, 2], ['foo', 'bar'], [3]] 242 | 243 | suite 'errors' -> 244 | test 'no type defined' -> 245 | throws (-> parsed-type-check [{}], true), /No type defined\. Input: true/ 246 | 247 | test 'types must be in array' -> 248 | throws (-> parsed-type-check {}, true), /Types must be in an array\. Input: true/ 249 | -------------------------------------------------------------------------------- /test/parse-type.ls: -------------------------------------------------------------------------------- 1 | {deep-equal, throws}:assert = require 'assert' 2 | {parse-type: p} = require '..' 3 | 4 | suite 'parse type' -> 5 | test 'simple' -> 6 | deep-equal (p 'Number'), [type: 'Number'] 7 | 8 | test 'different characters' -> 9 | deep-equal (p '2T_and$'), [type: '2T_and$'] 10 | 11 | test 'Maybe' -> 12 | deep-equal (p 'Maybe Number'), [ 13 | * type: 'Undefined' 14 | * type: 'Null' 15 | * type: 'Number' 16 | ] 17 | deep-equal (p 'Maybe Null | Number'), [ 18 | * type: 'Undefined' 19 | * type: 'Null' 20 | * type: 'Number' 21 | ] 22 | deep-equal (p 'Maybe Undefined | String'), [ 23 | * type: 'Undefined' 24 | * type: 'Null' 25 | * type: 'String' 26 | ] 27 | 28 | test 'wildcard' -> 29 | deep-equal (p '*'), [type: '*'] 30 | deep-equal (p '[*]'), [ 31 | structure: 'array' 32 | of: [type: '*'] 33 | ] 34 | deep-equal (p '{x: *}'), [ 35 | structure: 'fields' 36 | of: 37 | x: [type: '*'] 38 | subset: false 39 | ] 40 | deep-equal (p '*{a:Number}'), [ 41 | type: '*' 42 | structure: 'fields' 43 | of: 44 | a: [type: 'Number'] 45 | subset: false 46 | ] 47 | 48 | suite 'multiple types' -> 49 | test 'one' -> 50 | deep-equal (p 'Number'), [type: 'Number'] 51 | 52 | test 'two' -> 53 | deep-equal (p 'Number | String'), [ 54 | * type: 'Number' 55 | * type: 'String' 56 | ] 57 | 58 | test 'three' -> 59 | deep-equal (p 'Number | String | Float'), [ 60 | * type: 'Number' 61 | * type: 'String' 62 | * type: 'Float' 63 | ] 64 | 65 | test 'two' -> 66 | deep-equal (p 'Number | Number'), [ 67 | * type: 'Number' 68 | ] 69 | 70 | suite 'optional comment' -> 71 | test 'basic' -> 72 | deep-equal (p 'x :: Number'), [ 73 | * type: 'Number' 74 | ] 75 | 76 | test 'multiple' -> 77 | deep-equal (p 'x :: Number | String'), [ 78 | * type: 'Number' 79 | * type: 'String' 80 | ] 81 | 82 | test 'structures' -> 83 | deep-equal (p 'list :: [Number]'), [ 84 | structure: 'array' 85 | of: [type: 'Number'] 86 | ] 87 | deep-equal (p '[element :: Number]'), [ 88 | structure: 'array' 89 | of: [type: 'Number'] 90 | ] 91 | 92 | test 'no comment specified' -> 93 | throws (-> p ':: Number'), /No comment before comment separator '::' found/ 94 | 95 | suite 'array structure' -> 96 | test 'simple' -> 97 | deep-equal (p '[Number]'), [ 98 | structure: 'array' 99 | of: [type: 'Number'] 100 | ] 101 | 102 | test 'nested' -> 103 | deep-equal (p '[[Number]]'), [ 104 | structure: 'array' 105 | of: [ 106 | structure: 'array' 107 | of: [type: 'Number'] 108 | ] 109 | ] 110 | 111 | test 'nested with multiple' -> 112 | deep-equal (p '[[Number] | [String]]'), [ 113 | structure: 'array' 114 | of: [ 115 | * structure: 'array' 116 | of: [type: 'Number'] 117 | * structure: 'array' 118 | of: [type: 'String'] 119 | ] 120 | ] 121 | 122 | suite 'array structure with type' -> 123 | test 'simple' -> 124 | deep-equal (p 'Int16Array[Int]'), [ 125 | type: 'Int16Array' 126 | structure: 'array' 127 | of: [type: 'Int'] 128 | ] 129 | 130 | test 'nested' -> 131 | deep-equal (p 'Array[Float32Array[Float]]'), [ 132 | type: 'Array' 133 | structure: 'array' 134 | of: [ 135 | type: 'Float32Array' 136 | structure: 'array' 137 | of: [type: 'Float'] 138 | ] 139 | ] 140 | 141 | suite 'tuple structure' -> 142 | test 'single' -> 143 | deep-equal (p '(Number)'), [ 144 | structure: 'tuple' 145 | of: [ 146 | [type: 'Number'] 147 | ] 148 | ] 149 | 150 | test 'double' -> 151 | deep-equal (p '(Number, String)'), [ 152 | structure: 'tuple' 153 | of: [ 154 | [type: 'Number'] 155 | [type: 'String'] 156 | ] 157 | ] 158 | 159 | test 'trailing comma' -> 160 | deep-equal (p '(Number, String,)'), [ 161 | structure: 'tuple' 162 | of: [ 163 | [type: 'Number'] 164 | [type: 'String'] 165 | ] 166 | ] 167 | 168 | test 'nested' -> 169 | deep-equal (p '((Number, String), (Float))'), [ 170 | structure: 'tuple' 171 | of: 172 | * [{ 173 | structure: 'tuple' 174 | of: 175 | * [type: 'Number'] 176 | * [type: 'String'] 177 | }] 178 | * [{ 179 | structure: 'tuple' 180 | of: [[type: 'Float']] 181 | }] 182 | ] 183 | 184 | suite 'tuple structure with type' -> 185 | test 'double' -> 186 | deep-equal (p 'Type(Number, String)'), [ 187 | type: 'Type' 188 | structure: 'tuple' 189 | of: [ 190 | [type: 'Number'] 191 | [type: 'String'] 192 | ] 193 | ] 194 | 195 | test 'nested' -> 196 | deep-equal (p 'Type(Type2(Number, String), Type3(Float))'), [ 197 | type: 'Type' 198 | structure: 'tuple' 199 | of: 200 | * [{ 201 | type: 'Type2' 202 | structure: 'tuple' 203 | of: 204 | * [type: 'Number'] 205 | * [type: 'String'] 206 | }] 207 | * [{ 208 | type: 'Type3' 209 | structure: 'tuple' 210 | of: [[type: 'Float']] 211 | }] 212 | ] 213 | 214 | 215 | suite 'fields structure, without type' -> 216 | test 'simple' -> 217 | deep-equal (p '{a:Number, b:String}'), [ 218 | structure: 'fields' 219 | of: 220 | a: [type: 'Number'] 221 | b: [type: 'String'] 222 | subset: false 223 | ] 224 | 225 | test 'trailing comma' -> 226 | deep-equal (p '{a:Number, b:String,}'), [ 227 | structure: 'fields' 228 | of: 229 | a: [type: 'Number'] 230 | b: [type: 'String'] 231 | subset: false 232 | ] 233 | 234 | test 'nested' -> 235 | deep-equal (p '{a: {message: String}, b:String}'), [ 236 | structure: 'fields' 237 | of: 238 | a: [{ 239 | structure: 'fields' 240 | of: 241 | message: [type: 'String'] 242 | subset: false 243 | }] 244 | b: [type: 'String'] 245 | subset: false 246 | ] 247 | 248 | test 'subset' -> 249 | deep-equal (p '{a:Number, ...}'), [ 250 | structure: 'fields' 251 | of: 252 | a: [type: 'Number'] 253 | subset: true 254 | ] 255 | 256 | test 'no fields specified' -> 257 | deep-equal (p '{...}'), [ 258 | structure: 'fields' 259 | of: {} 260 | subset: true 261 | ] 262 | 263 | suite 'fields structure, with type' -> 264 | test 'simple' -> 265 | deep-equal (p 'Object{a:Number, b:String}'), [ 266 | type: 'Object' 267 | structure: 'fields' 268 | of: 269 | a: [type: 'Number'] 270 | b: [type: 'String'] 271 | subset: false 272 | ] 273 | 274 | test 'nested' -> 275 | deep-equal (p 'Object{a: Error{message: String}, b:String}'), [ 276 | type: 'Object' 277 | structure: 'fields' 278 | of: 279 | a: [{ 280 | type: 'Error' 281 | structure: 'fields' 282 | of: 283 | message: [type: 'String'] 284 | subset: false 285 | }] 286 | b: [type: 'String'] 287 | subset: false 288 | ] 289 | 290 | test 'subset' -> 291 | deep-equal (p 'Node{a:Number, ...}'), [ 292 | type: 'Node' 293 | structure: 'fields' 294 | of: 295 | a: [type: 'Number'] 296 | subset: true 297 | ] 298 | 299 | test 'no fields specified' -> 300 | deep-equal (p 'Date{...}'), [ 301 | type: 'Date' 302 | structure: 'fields' 303 | of: {} 304 | subset: true 305 | ] 306 | 307 | suite 'errors' -> 308 | test 'no type specified' -> 309 | throws (-> p ''), /No type specified/ 310 | 311 | test 'tuple of length 0' -> 312 | throws (-> p '()'), /Tuple must be of at least length 1/ 313 | 314 | test 'array without type' -> 315 | throws (-> p '[]'), /Must specify type of Array/ 316 | 317 | test 'unexpected end of input' -> 318 | throws (-> p ' '), /Unexpected end of input/ 319 | throws (-> p '['), /Unexpected end of input/ 320 | throws (-> p '[Number'), /Unexpected end of input/ 321 | throws (-> p '{'), /Unexpected end of input/ 322 | 323 | test 'unexpected end of input (input never tokenized)' -> 324 | throws (-> p '{Number:'), /Unexpected end of input/ 325 | 326 | test 'unexpected character' -> 327 | throws (-> p '[)'), /Unexpected character: \)/ 328 | throws (-> p '^'), /Unexpected character: \^/ 329 | 330 | test 'function types not supported' -> 331 | throws (-> p 'Number -> String'), /Function types are not supported. To validate that something is a function, you may use 'Function'/ 332 | 333 | test 'expected op' -> 334 | throws (-> p '[Number)'), /Expected '\]', got '\)' instead/ 335 | 336 | test 'expected text' -> 337 | throws (-> p '{:Number}'), /Expected text, got ':' instead/ 338 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # type-check [![Build Status](https://travis-ci.org/gkz/type-check.png?branch=master)](https://travis-ci.org/gkz/type-check) 2 | 3 | 4 | 5 | `type-check` is a library which allows you to check the types of JavaScript values at runtime with a Haskell like type syntax. It is great for checking external input, for testing, or even for adding a bit of safety to your internal code. It is a major component of [levn](https://github.com/gkz/levn). MIT license. Version 0.4.0. Check out the [demo](http://gkz.github.io/type-check/). 6 | 7 | For updates on `type-check`, [follow me on twitter](https://twitter.com/gkzahariev). 8 | 9 | npm install type-check 10 | 11 | ## Quick Examples 12 | 13 | ```js 14 | // Basic types: 15 | var typeCheck = require('type-check').typeCheck; 16 | typeCheck('Number', 1); // true 17 | typeCheck('Number', 'str'); // false 18 | typeCheck('Error', new Error); // true 19 | typeCheck('Undefined', undefined); // true 20 | 21 | // Comment 22 | typeCheck('count::Number', 1); // true 23 | 24 | // One type OR another type: 25 | typeCheck('Number | String', 2); // true 26 | typeCheck('Number | String', 'str'); // true 27 | 28 | // Wildcard, matches all types: 29 | typeCheck('*', 2) // true 30 | 31 | // Array, all elements of a single type: 32 | typeCheck('[Number]', [1, 2, 3]); // true 33 | typeCheck('[Number]', [1, 'str', 3]); // false 34 | 35 | // Tuples, or fixed length arrays with elements of different types: 36 | typeCheck('(String, Number)', ['str', 2]); // true 37 | typeCheck('(String, Number)', ['str']); // false 38 | typeCheck('(String, Number)', ['str', 2, 5]); // false 39 | 40 | // Object properties: 41 | typeCheck('{x: Number, y: Boolean}', {x: 2, y: false}); // true 42 | typeCheck('{x: Number, y: Boolean}', {x: 2}); // false 43 | typeCheck('{x: Number, y: Maybe Boolean}', {x: 2}); // true 44 | typeCheck('{x: Number, y: Boolean}', {x: 2, y: false, z: 3}); // false 45 | typeCheck('{x: Number, y: Boolean, ...}', {x: 2, y: false, z: 3}); // true 46 | 47 | // A particular type AND object properties: 48 | typeCheck('RegExp{source: String, ...}', /re/i); // true 49 | typeCheck('RegExp{source: String, ...}', {source: 're'}); // false 50 | 51 | // Custom types: 52 | var opt = {customTypes: 53 | {Even: { typeOf: 'Number', validate: function(x) { return x % 2 === 0; }}}}; 54 | typeCheck('Even', 2, opt); // true 55 | 56 | // Nested: 57 | var type = '{a: (String, [Number], {y: Array, ...}), b: Error{message: String, ...}}' 58 | typeCheck(type, {a: ['hi', [1, 2, 3], {y: [1, 'ms']}], b: new Error('oh no')}); // true 59 | ``` 60 | 61 | Check out the [type syntax format](#syntax) and [guide](#guide). 62 | 63 | ## Usage 64 | 65 | `require('type-check');` returns an object that exposes four properties. `VERSION` is the current version of the library as a string. `typeCheck`, `parseType`, and `parsedTypeCheck` are functions. 66 | 67 | ```js 68 | // typeCheck(type, input, options); 69 | typeCheck('Number', 2); // true 70 | 71 | // parseType(type); 72 | var parsedType = parseType('Number'); // object 73 | 74 | // parsedTypeCheck(parsedType, input, options); 75 | parsedTypeCheck(parsedType, 2); // true 76 | ``` 77 | 78 | ### typeCheck(type, input, options) 79 | 80 | `typeCheck` checks a JavaScript value `input` against `type` written in the [type format](#type-format) (and taking account the optional `options`) and returns whether the `input` matches the `type`. 81 | 82 | ##### arguments 83 | * type - `String` - the type written in the [type format](#type-format) which to check against 84 | * input - `*` - any JavaScript value, which is to be checked against the type 85 | * options - `Maybe Object` - an optional parameter specifying additional options, currently the only available option is specifying [custom types](#custom-types) 86 | 87 | ##### returns 88 | `Boolean` - whether the input matches the type 89 | 90 | ##### example 91 | ```js 92 | typeCheck('Number', 2); // true 93 | ``` 94 | 95 | ### parseType(type) 96 | 97 | `parseType` parses string `type` written in the [type format](#type-format) into an object representing the parsed type. 98 | 99 | ##### arguments 100 | * type - `String` - the type written in the [type format](#type-format) which to parse 101 | 102 | ##### returns 103 | `Object` - an object in the parsed type format representing the parsed type 104 | 105 | ##### example 106 | ```js 107 | parseType('Number'); // [{type: 'Number'}] 108 | ``` 109 | ### parsedTypeCheck(parsedType, input, options) 110 | 111 | `parsedTypeCheck` checks a JavaScript value `input` against parsed `type` in the parsed type format (and taking account the optional `options`) and returns whether the `input` matches the `type`. Use this in conjunction with `parseType` if you are going to use a type more than once. 112 | 113 | ##### arguments 114 | * type - `Object` - the type in the parsed type format which to check against 115 | * input - `*` - any JavaScript value, which is to be checked against the type 116 | * options - `Maybe Object` - an optional parameter specifying additional options, currently the only available option is specifying [custom types](#custom-types) 117 | 118 | ##### returns 119 | `Boolean` - whether the input matches the type 120 | 121 | ##### example 122 | ```js 123 | parsedTypeCheck([{type: 'Number'}], 2); // true 124 | var parsedType = parseType('String'); 125 | parsedTypeCheck(parsedType, 'str'); // true 126 | ``` 127 | 128 | 129 | ## Type Format 130 | 131 | ### Syntax 132 | 133 | White space is ignored. The root node is a __Types__. 134 | 135 | * __Identifier__ = `[\$\w]+` - a group of any lower or upper case letters, numbers, underscores, or dollar signs - eg. `String` 136 | * __Type__ = an `Identifier`, an `Identifier` followed by a `Structure`, just a `Structure`, or a wildcard `*` - eg. `String`, `Object{x: Number}`, `{x: Number}`, `Array{0: String, 1: Boolean, length: Number}`, `*` 137 | * __Types__ = optionally a comment (an `Identifier` followed by a `::`), optionally the identifier `Maybe`, one or more `Type`, separated by `|` - eg. `Number`, `String | Date`, `Maybe Number`, `Maybe Boolean | String` 138 | * __Structure__ = `Fields`, or a `Tuple`, or an `Array` - eg. `{x: Number}`, `(String, Number)`, `[Date]` 139 | * __Fields__ = a `{`, followed one or more `Field` separated by a comma `,` (trailing comma `,` is permitted), optionally an `...` (always preceded by a comma `,`), followed by a `}` - eg. `{x: Number, y: String}`, `{k: Function, ...}` 140 | * __Field__ = an `Identifier`, followed by a colon `:`, followed by `Types` - eg. `x: Date | String`, `y: Boolean` 141 | * __Tuple__ = a `(`, followed by one or more `Types` separated by a comma `,` (trailing comma `,` is permitted), followed by a `)` - eg `(Date)`, `(Number, Date)` 142 | * __Array__ = a `[` followed by exactly one `Types` followed by a `]` - eg. `[Boolean]`, `[Boolean | Null]` 143 | 144 | ### Guide 145 | 146 | `type-check` uses `Object.toString` to find out the basic type of a value. Specifically, 147 | 148 | ```js 149 | {}.toString.call(VALUE).slice(8, -1) 150 | {}.toString.call(true).slice(8, -1) // 'Boolean' 151 | ``` 152 | A basic type, eg. `Number`, uses this check. This is much more versatile than using `typeof` - for example, with `document`, `typeof` produces `'object'` which isn't that useful, and our technique produces `'HTMLDocument'`. 153 | 154 | You may check for multiple types by separating types with a `|`. The checker proceeds from left to right, and passes if the value is any of the types - eg. `String | Boolean` first checks if the value is a string, and then if it is a boolean. If it is none of those, then it returns false. 155 | 156 | Adding a `Maybe` in front of a list of multiple types is the same as also checking for `Null` and `Undefined` - eg. `Maybe String` is equivalent to `Undefined | Null | String`. 157 | 158 | You may add a comment to remind you of what the type is for by following an identifier with a `::` before a type (or multiple types). The comment is simply thrown out. 159 | 160 | The wildcard `*` matches all types. 161 | 162 | There are three types of structures for checking the contents of a value: 'fields', 'tuple', and 'array'. 163 | 164 | If used by itself, a 'fields' structure will pass with any type of object as long as it is an instance of `Object` and the properties pass - this allows for duck typing - eg. `{x: Boolean}`. 165 | 166 | To check if the properties pass, and the value is of a certain type, you can specify the type - eg. `Error{message: String}`. 167 | 168 | If you want to make a field optional, you can simply use `Maybe` - eg. `{x: Boolean, y: Maybe String}` will still pass if `y` is undefined (or null). 169 | 170 | If you don't care if the value has properties beyond what you have specified, you can use the 'etc' operator `...` - eg. `{x: Boolean, ...}` will match an object with an `x` property that is a boolean, and with zero or more other properties. 171 | 172 | For an array, you must specify one or more types (separated by `|`) - it will pass for something of any length as long as each element passes the types provided - eg. `[Number]`, `[Number | String]`. 173 | 174 | A tuple checks for a fixed number of elements, each of a potentially different type. Each element is separated by a comma - eg. `(String, Number)`. 175 | 176 | An array and tuple structure check that the value is of type `Array` by default, but if another type is specified, they will check for that instead - eg. `Int32Array[Number]`. You can use the wildcard `*` to search for any type at all. 177 | 178 | Check out the [type precedence](https://github.com/zaboco/type-precedence) library for type-check. 179 | 180 | ## Options 181 | 182 | Options is an object. It is an optional parameter to the `typeCheck` and `parsedTypeCheck` functions. The only current option is `customTypes`. 183 | 184 | 185 | ### Custom Types 186 | 187 | __Example:__ 188 | 189 | ```js 190 | var options = { 191 | customTypes: { 192 | Even: { 193 | typeOf: 'Number', 194 | validate: function(x) { 195 | return x % 2 === 0; 196 | } 197 | } 198 | } 199 | }; 200 | typeCheck('Even', 2, options); // true 201 | typeCheck('Even', 3, options); // false 202 | ``` 203 | 204 | `customTypes` allows you to set up custom types for validation. The value of this is an object. The keys of the object are the types you will be matching. Each value of the object will be an object having a `typeOf` property - a string, and `validate` property - a function. 205 | 206 | The `typeOf` property is the type the value should be (optional - if not set only `validate` will be used), and `validate` is a function which should return true if the value is of that type. `validate` receives one parameter, which is the value that we are checking. 207 | 208 | ## Technical About 209 | 210 | `type-check` is written in [LiveScript](http://livescript.net/) - a language that compiles to JavaScript. It also uses the [prelude.ls](http://preludels.com/) library. 211 | -------------------------------------------------------------------------------- /browser/type-check.js: -------------------------------------------------------------------------------- 1 | // Generated by LiveScript 1.6.0 2 | 3 | // type-check 0.4.0 4 | // Copyright (c) George Zahariev 5 | // Released under the MIT License 6 | // https://raw.githubusercontent.com/gkz/type-check/master/LICENSE 7 | require=(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i|' + identifierRegex.source + '|\\S', 'g'); 314 | module.exports = function(input){ 315 | var tokens, e; 316 | if (!input.length) { 317 | throw new Error('No type specified.'); 318 | } 319 | tokens = input.match(tokenRegex) || []; 320 | if (in$('->', tokens)) { 321 | throw new Error("Function types are not supported.\ To validate that something is a function, you may use 'Function'."); 322 | } 323 | try { 324 | return consumeTypes(tokens); 325 | } catch (e$) { 326 | e = e$; 327 | throw new Error(e.message + " - Remaining tokens: " + JSON.stringify(tokens) + " - Initial input: '" + input + "'"); 328 | } 329 | }; 330 | function in$(x, xs){ 331 | var i = -1, l = xs.length >>> 0; 332 | while (++i < l) if (x === xs[i]) return true; 333 | return false; 334 | } 335 | }).call(this); 336 | 337 | },{}],3:[function(require,module,exports){ 338 | // Generated by LiveScript 1.6.0 339 | var apply, curry, flip, fix, over, memoize, toString$ = {}.toString; 340 | apply = curry$(function(f, list){ 341 | return f.apply(null, list); 342 | }); 343 | curry = function(f){ 344 | return curry$(f); 345 | }; 346 | flip = curry$(function(f, x, y){ 347 | return f(y, x); 348 | }); 349 | fix = function(f){ 350 | return function(g){ 351 | return function(){ 352 | return f(g(g)).apply(null, arguments); 353 | }; 354 | }(function(g){ 355 | return function(){ 356 | return f(g(g)).apply(null, arguments); 357 | }; 358 | }); 359 | }; 360 | over = curry$(function(f, g, x, y){ 361 | return f(g(x), g(y)); 362 | }); 363 | memoize = function(f){ 364 | var memo; 365 | memo = {}; 366 | return function(){ 367 | var args, res$, i$, to$, key, arg; 368 | res$ = []; 369 | for (i$ = 0, to$ = arguments.length; i$ < to$; ++i$) { 370 | res$.push(arguments[i$]); 371 | } 372 | args = res$; 373 | key = (function(){ 374 | var i$, ref$, len$, results$ = []; 375 | for (i$ = 0, len$ = (ref$ = args).length; i$ < len$; ++i$) { 376 | arg = ref$[i$]; 377 | results$.push(arg + toString$.call(arg).slice(8, -1)); 378 | } 379 | return results$; 380 | }()).join(''); 381 | return memo[key] = key in memo 382 | ? memo[key] 383 | : f.apply(null, args); 384 | }; 385 | }; 386 | module.exports = { 387 | curry: curry, 388 | flip: flip, 389 | fix: fix, 390 | apply: apply, 391 | over: over, 392 | memoize: memoize 393 | }; 394 | function curry$(f, bound){ 395 | var context, 396 | _curry = function(args) { 397 | return f.length > 1 ? function(){ 398 | var params = args ? args.concat() : []; 399 | context = bound ? context || this : this; 400 | return params.push.apply(params, arguments) < 401 | f.length && arguments.length ? 402 | _curry.call(context, params) : f.apply(context, params); 403 | } : f; 404 | }; 405 | return _curry(); 406 | } 407 | },{}],4:[function(require,module,exports){ 408 | // Generated by LiveScript 1.6.0 409 | var each, map, compact, filter, reject, remove, partition, find, head, first, tail, last, initial, empty, reverse, unique, uniqueBy, fold, foldl, fold1, foldl1, foldr, foldr1, unfoldr, concat, concatMap, flatten, difference, intersection, union, countBy, groupBy, andList, orList, any, all, sort, sortWith, sortBy, sum, product, mean, average, maximum, minimum, maximumBy, minimumBy, scan, scanl, scan1, scanl1, scanr, scanr1, slice, take, drop, splitAt, takeWhile, dropWhile, span, breakList, zip, zipWith, zipAll, zipAllWith, at, elemIndex, elemIndices, findIndex, findIndices, toString$ = {}.toString; 410 | each = curry$(function(f, xs){ 411 | var i$, len$, x; 412 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 413 | x = xs[i$]; 414 | f(x); 415 | } 416 | return xs; 417 | }); 418 | map = curry$(function(f, xs){ 419 | var i$, len$, x, results$ = []; 420 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 421 | x = xs[i$]; 422 | results$.push(f(x)); 423 | } 424 | return results$; 425 | }); 426 | compact = function(xs){ 427 | var i$, len$, x, results$ = []; 428 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 429 | x = xs[i$]; 430 | if (x) { 431 | results$.push(x); 432 | } 433 | } 434 | return results$; 435 | }; 436 | filter = curry$(function(f, xs){ 437 | var i$, len$, x, results$ = []; 438 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 439 | x = xs[i$]; 440 | if (f(x)) { 441 | results$.push(x); 442 | } 443 | } 444 | return results$; 445 | }); 446 | reject = curry$(function(f, xs){ 447 | var i$, len$, x, results$ = []; 448 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 449 | x = xs[i$]; 450 | if (!f(x)) { 451 | results$.push(x); 452 | } 453 | } 454 | return results$; 455 | }); 456 | remove = curry$(function(el, xs){ 457 | var i, x$; 458 | i = elemIndex(el, xs); 459 | x$ = xs.slice(); 460 | if (i != null) { 461 | x$.splice(i, 1); 462 | } 463 | return x$; 464 | }); 465 | partition = curry$(function(f, xs){ 466 | var passed, failed, i$, len$, x; 467 | passed = []; 468 | failed = []; 469 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 470 | x = xs[i$]; 471 | (f(x) ? passed : failed).push(x); 472 | } 473 | return [passed, failed]; 474 | }); 475 | find = curry$(function(f, xs){ 476 | var i$, len$, x; 477 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 478 | x = xs[i$]; 479 | if (f(x)) { 480 | return x; 481 | } 482 | } 483 | }); 484 | head = first = function(xs){ 485 | return xs[0]; 486 | }; 487 | tail = function(xs){ 488 | if (!xs.length) { 489 | return; 490 | } 491 | return xs.slice(1); 492 | }; 493 | last = function(xs){ 494 | return xs[xs.length - 1]; 495 | }; 496 | initial = function(xs){ 497 | if (!xs.length) { 498 | return; 499 | } 500 | return xs.slice(0, -1); 501 | }; 502 | empty = function(xs){ 503 | return !xs.length; 504 | }; 505 | reverse = function(xs){ 506 | return xs.concat().reverse(); 507 | }; 508 | unique = function(xs){ 509 | var result, i$, len$, x; 510 | result = []; 511 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 512 | x = xs[i$]; 513 | if (!in$(x, result)) { 514 | result.push(x); 515 | } 516 | } 517 | return result; 518 | }; 519 | uniqueBy = curry$(function(f, xs){ 520 | var seen, i$, len$, x, val, results$ = []; 521 | seen = []; 522 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 523 | x = xs[i$]; 524 | val = f(x); 525 | if (in$(val, seen)) { 526 | continue; 527 | } 528 | seen.push(val); 529 | results$.push(x); 530 | } 531 | return results$; 532 | }); 533 | fold = foldl = curry$(function(f, memo, xs){ 534 | var i$, len$, x; 535 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 536 | x = xs[i$]; 537 | memo = f(memo, x); 538 | } 539 | return memo; 540 | }); 541 | fold1 = foldl1 = curry$(function(f, xs){ 542 | return fold(f, xs[0], xs.slice(1)); 543 | }); 544 | foldr = curry$(function(f, memo, xs){ 545 | var i$, x; 546 | for (i$ = xs.length - 1; i$ >= 0; --i$) { 547 | x = xs[i$]; 548 | memo = f(x, memo); 549 | } 550 | return memo; 551 | }); 552 | foldr1 = curry$(function(f, xs){ 553 | return foldr(f, xs[xs.length - 1], xs.slice(0, -1)); 554 | }); 555 | unfoldr = curry$(function(f, b){ 556 | var result, x, that; 557 | result = []; 558 | x = b; 559 | while ((that = f(x)) != null) { 560 | result.push(that[0]); 561 | x = that[1]; 562 | } 563 | return result; 564 | }); 565 | concat = function(xss){ 566 | return [].concat.apply([], xss); 567 | }; 568 | concatMap = curry$(function(f, xs){ 569 | var x; 570 | return [].concat.apply([], (function(){ 571 | var i$, ref$, len$, results$ = []; 572 | for (i$ = 0, len$ = (ref$ = xs).length; i$ < len$; ++i$) { 573 | x = ref$[i$]; 574 | results$.push(f(x)); 575 | } 576 | return results$; 577 | }())); 578 | }); 579 | flatten = function(xs){ 580 | var x; 581 | return [].concat.apply([], (function(){ 582 | var i$, ref$, len$, results$ = []; 583 | for (i$ = 0, len$ = (ref$ = xs).length; i$ < len$; ++i$) { 584 | x = ref$[i$]; 585 | if (toString$.call(x).slice(8, -1) === 'Array') { 586 | results$.push(flatten(x)); 587 | } else { 588 | results$.push(x); 589 | } 590 | } 591 | return results$; 592 | }())); 593 | }; 594 | difference = function(xs){ 595 | var yss, res$, i$, to$, results, len$, x, j$, len1$, ys; 596 | res$ = []; 597 | for (i$ = 1, to$ = arguments.length; i$ < to$; ++i$) { 598 | res$.push(arguments[i$]); 599 | } 600 | yss = res$; 601 | results = []; 602 | outer: for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 603 | x = xs[i$]; 604 | for (j$ = 0, len1$ = yss.length; j$ < len1$; ++j$) { 605 | ys = yss[j$]; 606 | if (in$(x, ys)) { 607 | continue outer; 608 | } 609 | } 610 | results.push(x); 611 | } 612 | return results; 613 | }; 614 | intersection = function(xs){ 615 | var yss, res$, i$, to$, results, len$, x, j$, len1$, ys; 616 | res$ = []; 617 | for (i$ = 1, to$ = arguments.length; i$ < to$; ++i$) { 618 | res$.push(arguments[i$]); 619 | } 620 | yss = res$; 621 | results = []; 622 | outer: for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 623 | x = xs[i$]; 624 | for (j$ = 0, len1$ = yss.length; j$ < len1$; ++j$) { 625 | ys = yss[j$]; 626 | if (!in$(x, ys)) { 627 | continue outer; 628 | } 629 | } 630 | results.push(x); 631 | } 632 | return results; 633 | }; 634 | union = function(){ 635 | var xss, res$, i$, to$, results, len$, xs, j$, len1$, x; 636 | res$ = []; 637 | for (i$ = 0, to$ = arguments.length; i$ < to$; ++i$) { 638 | res$.push(arguments[i$]); 639 | } 640 | xss = res$; 641 | results = []; 642 | for (i$ = 0, len$ = xss.length; i$ < len$; ++i$) { 643 | xs = xss[i$]; 644 | for (j$ = 0, len1$ = xs.length; j$ < len1$; ++j$) { 645 | x = xs[j$]; 646 | if (!in$(x, results)) { 647 | results.push(x); 648 | } 649 | } 650 | } 651 | return results; 652 | }; 653 | countBy = curry$(function(f, xs){ 654 | var results, i$, len$, x, key; 655 | results = {}; 656 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 657 | x = xs[i$]; 658 | key = f(x); 659 | if (key in results) { 660 | results[key] += 1; 661 | } else { 662 | results[key] = 1; 663 | } 664 | } 665 | return results; 666 | }); 667 | groupBy = curry$(function(f, xs){ 668 | var results, i$, len$, x, key; 669 | results = {}; 670 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 671 | x = xs[i$]; 672 | key = f(x); 673 | if (key in results) { 674 | results[key].push(x); 675 | } else { 676 | results[key] = [x]; 677 | } 678 | } 679 | return results; 680 | }); 681 | andList = function(xs){ 682 | var i$, len$, x; 683 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 684 | x = xs[i$]; 685 | if (!x) { 686 | return false; 687 | } 688 | } 689 | return true; 690 | }; 691 | orList = function(xs){ 692 | var i$, len$, x; 693 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 694 | x = xs[i$]; 695 | if (x) { 696 | return true; 697 | } 698 | } 699 | return false; 700 | }; 701 | any = curry$(function(f, xs){ 702 | var i$, len$, x; 703 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 704 | x = xs[i$]; 705 | if (f(x)) { 706 | return true; 707 | } 708 | } 709 | return false; 710 | }); 711 | all = curry$(function(f, xs){ 712 | var i$, len$, x; 713 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 714 | x = xs[i$]; 715 | if (!f(x)) { 716 | return false; 717 | } 718 | } 719 | return true; 720 | }); 721 | sort = function(xs){ 722 | return xs.concat().sort(function(x, y){ 723 | if (x > y) { 724 | return 1; 725 | } else if (x < y) { 726 | return -1; 727 | } else { 728 | return 0; 729 | } 730 | }); 731 | }; 732 | sortWith = curry$(function(f, xs){ 733 | return xs.concat().sort(f); 734 | }); 735 | sortBy = curry$(function(f, xs){ 736 | return xs.concat().sort(function(x, y){ 737 | if (f(x) > f(y)) { 738 | return 1; 739 | } else if (f(x) < f(y)) { 740 | return -1; 741 | } else { 742 | return 0; 743 | } 744 | }); 745 | }); 746 | sum = function(xs){ 747 | var result, i$, len$, x; 748 | result = 0; 749 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 750 | x = xs[i$]; 751 | result += x; 752 | } 753 | return result; 754 | }; 755 | product = function(xs){ 756 | var result, i$, len$, x; 757 | result = 1; 758 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 759 | x = xs[i$]; 760 | result *= x; 761 | } 762 | return result; 763 | }; 764 | mean = average = function(xs){ 765 | var sum, i$, len$, x; 766 | sum = 0; 767 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 768 | x = xs[i$]; 769 | sum += x; 770 | } 771 | return sum / xs.length; 772 | }; 773 | maximum = function(xs){ 774 | var max, i$, ref$, len$, x; 775 | max = xs[0]; 776 | for (i$ = 0, len$ = (ref$ = xs.slice(1)).length; i$ < len$; ++i$) { 777 | x = ref$[i$]; 778 | if (x > max) { 779 | max = x; 780 | } 781 | } 782 | return max; 783 | }; 784 | minimum = function(xs){ 785 | var min, i$, ref$, len$, x; 786 | min = xs[0]; 787 | for (i$ = 0, len$ = (ref$ = xs.slice(1)).length; i$ < len$; ++i$) { 788 | x = ref$[i$]; 789 | if (x < min) { 790 | min = x; 791 | } 792 | } 793 | return min; 794 | }; 795 | maximumBy = curry$(function(f, xs){ 796 | var max, i$, ref$, len$, x; 797 | max = xs[0]; 798 | for (i$ = 0, len$ = (ref$ = xs.slice(1)).length; i$ < len$; ++i$) { 799 | x = ref$[i$]; 800 | if (f(x) > f(max)) { 801 | max = x; 802 | } 803 | } 804 | return max; 805 | }); 806 | minimumBy = curry$(function(f, xs){ 807 | var min, i$, ref$, len$, x; 808 | min = xs[0]; 809 | for (i$ = 0, len$ = (ref$ = xs.slice(1)).length; i$ < len$; ++i$) { 810 | x = ref$[i$]; 811 | if (f(x) < f(min)) { 812 | min = x; 813 | } 814 | } 815 | return min; 816 | }); 817 | scan = scanl = curry$(function(f, memo, xs){ 818 | var last, x; 819 | last = memo; 820 | return [memo].concat((function(){ 821 | var i$, ref$, len$, results$ = []; 822 | for (i$ = 0, len$ = (ref$ = xs).length; i$ < len$; ++i$) { 823 | x = ref$[i$]; 824 | results$.push(last = f(last, x)); 825 | } 826 | return results$; 827 | }())); 828 | }); 829 | scan1 = scanl1 = curry$(function(f, xs){ 830 | if (!xs.length) { 831 | return; 832 | } 833 | return scan(f, xs[0], xs.slice(1)); 834 | }); 835 | scanr = curry$(function(f, memo, xs){ 836 | xs = xs.concat().reverse(); 837 | return scan(f, memo, xs).reverse(); 838 | }); 839 | scanr1 = curry$(function(f, xs){ 840 | if (!xs.length) { 841 | return; 842 | } 843 | xs = xs.concat().reverse(); 844 | return scan(f, xs[0], xs.slice(1)).reverse(); 845 | }); 846 | slice = curry$(function(x, y, xs){ 847 | return xs.slice(x, y); 848 | }); 849 | take = curry$(function(n, xs){ 850 | if (n <= 0) { 851 | return xs.slice(0, 0); 852 | } else { 853 | return xs.slice(0, n); 854 | } 855 | }); 856 | drop = curry$(function(n, xs){ 857 | if (n <= 0) { 858 | return xs; 859 | } else { 860 | return xs.slice(n); 861 | } 862 | }); 863 | splitAt = curry$(function(n, xs){ 864 | return [take(n, xs), drop(n, xs)]; 865 | }); 866 | takeWhile = curry$(function(p, xs){ 867 | var len, i; 868 | len = xs.length; 869 | if (!len) { 870 | return xs; 871 | } 872 | i = 0; 873 | while (i < len && p(xs[i])) { 874 | i += 1; 875 | } 876 | return xs.slice(0, i); 877 | }); 878 | dropWhile = curry$(function(p, xs){ 879 | var len, i; 880 | len = xs.length; 881 | if (!len) { 882 | return xs; 883 | } 884 | i = 0; 885 | while (i < len && p(xs[i])) { 886 | i += 1; 887 | } 888 | return xs.slice(i); 889 | }); 890 | span = curry$(function(p, xs){ 891 | return [takeWhile(p, xs), dropWhile(p, xs)]; 892 | }); 893 | breakList = curry$(function(p, xs){ 894 | return span(compose$(p, not$), xs); 895 | }); 896 | zip = curry$(function(xs, ys){ 897 | var result, len, i$, len$, i, x; 898 | result = []; 899 | len = ys.length; 900 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 901 | i = i$; 902 | x = xs[i$]; 903 | if (i === len) { 904 | break; 905 | } 906 | result.push([x, ys[i]]); 907 | } 908 | return result; 909 | }); 910 | zipWith = curry$(function(f, xs, ys){ 911 | var result, len, i$, len$, i, x; 912 | result = []; 913 | len = ys.length; 914 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 915 | i = i$; 916 | x = xs[i$]; 917 | if (i === len) { 918 | break; 919 | } 920 | result.push(f(x, ys[i])); 921 | } 922 | return result; 923 | }); 924 | zipAll = function(){ 925 | var xss, res$, i$, to$, minLength, len$, xs, ref$, i, lresult$, j$, results$ = []; 926 | res$ = []; 927 | for (i$ = 0, to$ = arguments.length; i$ < to$; ++i$) { 928 | res$.push(arguments[i$]); 929 | } 930 | xss = res$; 931 | minLength = undefined; 932 | for (i$ = 0, len$ = xss.length; i$ < len$; ++i$) { 933 | xs = xss[i$]; 934 | minLength <= (ref$ = xs.length) || (minLength = ref$); 935 | } 936 | for (i$ = 0; i$ < minLength; ++i$) { 937 | i = i$; 938 | lresult$ = []; 939 | for (j$ = 0, len$ = xss.length; j$ < len$; ++j$) { 940 | xs = xss[j$]; 941 | lresult$.push(xs[i]); 942 | } 943 | results$.push(lresult$); 944 | } 945 | return results$; 946 | }; 947 | zipAllWith = function(f){ 948 | var xss, res$, i$, to$, minLength, len$, xs, ref$, i, results$ = []; 949 | res$ = []; 950 | for (i$ = 1, to$ = arguments.length; i$ < to$; ++i$) { 951 | res$.push(arguments[i$]); 952 | } 953 | xss = res$; 954 | minLength = undefined; 955 | for (i$ = 0, len$ = xss.length; i$ < len$; ++i$) { 956 | xs = xss[i$]; 957 | minLength <= (ref$ = xs.length) || (minLength = ref$); 958 | } 959 | for (i$ = 0; i$ < minLength; ++i$) { 960 | i = i$; 961 | results$.push(f.apply(null, (fn$()))); 962 | } 963 | return results$; 964 | function fn$(){ 965 | var i$, ref$, len$, results$ = []; 966 | for (i$ = 0, len$ = (ref$ = xss).length; i$ < len$; ++i$) { 967 | xs = ref$[i$]; 968 | results$.push(xs[i]); 969 | } 970 | return results$; 971 | } 972 | }; 973 | at = curry$(function(n, xs){ 974 | if (n < 0) { 975 | return xs[xs.length + n]; 976 | } else { 977 | return xs[n]; 978 | } 979 | }); 980 | elemIndex = curry$(function(el, xs){ 981 | var i$, len$, i, x; 982 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 983 | i = i$; 984 | x = xs[i$]; 985 | if (x === el) { 986 | return i; 987 | } 988 | } 989 | }); 990 | elemIndices = curry$(function(el, xs){ 991 | var i$, len$, i, x, results$ = []; 992 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 993 | i = i$; 994 | x = xs[i$]; 995 | if (x === el) { 996 | results$.push(i); 997 | } 998 | } 999 | return results$; 1000 | }); 1001 | findIndex = curry$(function(f, xs){ 1002 | var i$, len$, i, x; 1003 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 1004 | i = i$; 1005 | x = xs[i$]; 1006 | if (f(x)) { 1007 | return i; 1008 | } 1009 | } 1010 | }); 1011 | findIndices = curry$(function(f, xs){ 1012 | var i$, len$, i, x, results$ = []; 1013 | for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { 1014 | i = i$; 1015 | x = xs[i$]; 1016 | if (f(x)) { 1017 | results$.push(i); 1018 | } 1019 | } 1020 | return results$; 1021 | }); 1022 | module.exports = { 1023 | each: each, 1024 | map: map, 1025 | filter: filter, 1026 | compact: compact, 1027 | reject: reject, 1028 | remove: remove, 1029 | partition: partition, 1030 | find: find, 1031 | head: head, 1032 | first: first, 1033 | tail: tail, 1034 | last: last, 1035 | initial: initial, 1036 | empty: empty, 1037 | reverse: reverse, 1038 | difference: difference, 1039 | intersection: intersection, 1040 | union: union, 1041 | countBy: countBy, 1042 | groupBy: groupBy, 1043 | fold: fold, 1044 | fold1: fold1, 1045 | foldl: foldl, 1046 | foldl1: foldl1, 1047 | foldr: foldr, 1048 | foldr1: foldr1, 1049 | unfoldr: unfoldr, 1050 | andList: andList, 1051 | orList: orList, 1052 | any: any, 1053 | all: all, 1054 | unique: unique, 1055 | uniqueBy: uniqueBy, 1056 | sort: sort, 1057 | sortWith: sortWith, 1058 | sortBy: sortBy, 1059 | sum: sum, 1060 | product: product, 1061 | mean: mean, 1062 | average: average, 1063 | concat: concat, 1064 | concatMap: concatMap, 1065 | flatten: flatten, 1066 | maximum: maximum, 1067 | minimum: minimum, 1068 | maximumBy: maximumBy, 1069 | minimumBy: minimumBy, 1070 | scan: scan, 1071 | scan1: scan1, 1072 | scanl: scanl, 1073 | scanl1: scanl1, 1074 | scanr: scanr, 1075 | scanr1: scanr1, 1076 | slice: slice, 1077 | take: take, 1078 | drop: drop, 1079 | splitAt: splitAt, 1080 | takeWhile: takeWhile, 1081 | dropWhile: dropWhile, 1082 | span: span, 1083 | breakList: breakList, 1084 | zip: zip, 1085 | zipWith: zipWith, 1086 | zipAll: zipAll, 1087 | zipAllWith: zipAllWith, 1088 | at: at, 1089 | elemIndex: elemIndex, 1090 | elemIndices: elemIndices, 1091 | findIndex: findIndex, 1092 | findIndices: findIndices 1093 | }; 1094 | function curry$(f, bound){ 1095 | var context, 1096 | _curry = function(args) { 1097 | return f.length > 1 ? function(){ 1098 | var params = args ? args.concat() : []; 1099 | context = bound ? context || this : this; 1100 | return params.push.apply(params, arguments) < 1101 | f.length && arguments.length ? 1102 | _curry.call(context, params) : f.apply(context, params); 1103 | } : f; 1104 | }; 1105 | return _curry(); 1106 | } 1107 | function in$(x, xs){ 1108 | var i = -1, l = xs.length >>> 0; 1109 | while (++i < l) if (x === xs[i]) return true; 1110 | return false; 1111 | } 1112 | function compose$() { 1113 | var functions = arguments; 1114 | return function() { 1115 | var i, result; 1116 | result = functions[0].apply(this, arguments); 1117 | for (i = 1; i < functions.length; ++i) { 1118 | result = functions[i](result); 1119 | } 1120 | return result; 1121 | }; 1122 | } 1123 | function not$(x){ return !x; } 1124 | },{}],5:[function(require,module,exports){ 1125 | // Generated by LiveScript 1.6.0 1126 | var max, min, negate, abs, signum, quot, rem, div, mod, recip, pi, tau, exp, sqrt, ln, pow, sin, tan, cos, asin, acos, atan, atan2, truncate, round, ceiling, floor, isItNaN, even, odd, gcd, lcm; 1127 | max = curry$(function(x$, y$){ 1128 | return x$ > y$ ? x$ : y$; 1129 | }); 1130 | min = curry$(function(x$, y$){ 1131 | return x$ < y$ ? x$ : y$; 1132 | }); 1133 | negate = function(x){ 1134 | return -x; 1135 | }; 1136 | abs = Math.abs; 1137 | signum = function(x){ 1138 | if (x < 0) { 1139 | return -1; 1140 | } else if (x > 0) { 1141 | return 1; 1142 | } else { 1143 | return 0; 1144 | } 1145 | }; 1146 | quot = curry$(function(x, y){ 1147 | return ~~(x / y); 1148 | }); 1149 | rem = curry$(function(x$, y$){ 1150 | return x$ % y$; 1151 | }); 1152 | div = curry$(function(x, y){ 1153 | return Math.floor(x / y); 1154 | }); 1155 | mod = curry$(function(x$, y$){ 1156 | var ref$; 1157 | return ((x$) % (ref$ = y$) + ref$) % ref$; 1158 | }); 1159 | recip = (function(it){ 1160 | return 1 / it; 1161 | }); 1162 | pi = Math.PI; 1163 | tau = pi * 2; 1164 | exp = Math.exp; 1165 | sqrt = Math.sqrt; 1166 | ln = Math.log; 1167 | pow = curry$(function(x$, y$){ 1168 | return Math.pow(x$, y$); 1169 | }); 1170 | sin = Math.sin; 1171 | tan = Math.tan; 1172 | cos = Math.cos; 1173 | asin = Math.asin; 1174 | acos = Math.acos; 1175 | atan = Math.atan; 1176 | atan2 = curry$(function(x, y){ 1177 | return Math.atan2(x, y); 1178 | }); 1179 | truncate = function(x){ 1180 | return ~~x; 1181 | }; 1182 | round = Math.round; 1183 | ceiling = Math.ceil; 1184 | floor = Math.floor; 1185 | isItNaN = function(x){ 1186 | return x !== x; 1187 | }; 1188 | even = function(x){ 1189 | return x % 2 === 0; 1190 | }; 1191 | odd = function(x){ 1192 | return x % 2 !== 0; 1193 | }; 1194 | gcd = curry$(function(x, y){ 1195 | var z; 1196 | x = Math.abs(x); 1197 | y = Math.abs(y); 1198 | while (y !== 0) { 1199 | z = x % y; 1200 | x = y; 1201 | y = z; 1202 | } 1203 | return x; 1204 | }); 1205 | lcm = curry$(function(x, y){ 1206 | return Math.abs(Math.floor(x / gcd(x, y) * y)); 1207 | }); 1208 | module.exports = { 1209 | max: max, 1210 | min: min, 1211 | negate: negate, 1212 | abs: abs, 1213 | signum: signum, 1214 | quot: quot, 1215 | rem: rem, 1216 | div: div, 1217 | mod: mod, 1218 | recip: recip, 1219 | pi: pi, 1220 | tau: tau, 1221 | exp: exp, 1222 | sqrt: sqrt, 1223 | ln: ln, 1224 | pow: pow, 1225 | sin: sin, 1226 | tan: tan, 1227 | cos: cos, 1228 | acos: acos, 1229 | asin: asin, 1230 | atan: atan, 1231 | atan2: atan2, 1232 | truncate: truncate, 1233 | round: round, 1234 | ceiling: ceiling, 1235 | floor: floor, 1236 | isItNaN: isItNaN, 1237 | even: even, 1238 | odd: odd, 1239 | gcd: gcd, 1240 | lcm: lcm 1241 | }; 1242 | function curry$(f, bound){ 1243 | var context, 1244 | _curry = function(args) { 1245 | return f.length > 1 ? function(){ 1246 | var params = args ? args.concat() : []; 1247 | context = bound ? context || this : this; 1248 | return params.push.apply(params, arguments) < 1249 | f.length && arguments.length ? 1250 | _curry.call(context, params) : f.apply(context, params); 1251 | } : f; 1252 | }; 1253 | return _curry(); 1254 | } 1255 | },{}],6:[function(require,module,exports){ 1256 | // Generated by LiveScript 1.6.0 1257 | var values, keys, pairsToObj, objToPairs, listsToObj, objToLists, empty, each, map, compact, filter, reject, partition, find; 1258 | values = function(object){ 1259 | var i$, x, results$ = []; 1260 | for (i$ in object) { 1261 | x = object[i$]; 1262 | results$.push(x); 1263 | } 1264 | return results$; 1265 | }; 1266 | keys = function(object){ 1267 | var x, results$ = []; 1268 | for (x in object) { 1269 | results$.push(x); 1270 | } 1271 | return results$; 1272 | }; 1273 | pairsToObj = function(object){ 1274 | var i$, len$, x, resultObj$ = {}; 1275 | for (i$ = 0, len$ = object.length; i$ < len$; ++i$) { 1276 | x = object[i$]; 1277 | resultObj$[x[0]] = x[1]; 1278 | } 1279 | return resultObj$; 1280 | }; 1281 | objToPairs = function(object){ 1282 | var key, value, results$ = []; 1283 | for (key in object) { 1284 | value = object[key]; 1285 | results$.push([key, value]); 1286 | } 1287 | return results$; 1288 | }; 1289 | listsToObj = curry$(function(keys, values){ 1290 | var i$, len$, i, key, resultObj$ = {}; 1291 | for (i$ = 0, len$ = keys.length; i$ < len$; ++i$) { 1292 | i = i$; 1293 | key = keys[i$]; 1294 | resultObj$[key] = values[i]; 1295 | } 1296 | return resultObj$; 1297 | }); 1298 | objToLists = function(object){ 1299 | var keys, values, key, value; 1300 | keys = []; 1301 | values = []; 1302 | for (key in object) { 1303 | value = object[key]; 1304 | keys.push(key); 1305 | values.push(value); 1306 | } 1307 | return [keys, values]; 1308 | }; 1309 | empty = function(object){ 1310 | var x; 1311 | for (x in object) { 1312 | return false; 1313 | } 1314 | return true; 1315 | }; 1316 | each = curry$(function(f, object){ 1317 | var i$, x; 1318 | for (i$ in object) { 1319 | x = object[i$]; 1320 | f(x); 1321 | } 1322 | return object; 1323 | }); 1324 | map = curry$(function(f, object){ 1325 | var k, x, resultObj$ = {}; 1326 | for (k in object) { 1327 | x = object[k]; 1328 | resultObj$[k] = f(x); 1329 | } 1330 | return resultObj$; 1331 | }); 1332 | compact = function(object){ 1333 | var k, x, resultObj$ = {}; 1334 | for (k in object) { 1335 | x = object[k]; 1336 | if (x) { 1337 | resultObj$[k] = x; 1338 | } 1339 | } 1340 | return resultObj$; 1341 | }; 1342 | filter = curry$(function(f, object){ 1343 | var k, x, resultObj$ = {}; 1344 | for (k in object) { 1345 | x = object[k]; 1346 | if (f(x)) { 1347 | resultObj$[k] = x; 1348 | } 1349 | } 1350 | return resultObj$; 1351 | }); 1352 | reject = curry$(function(f, object){ 1353 | var k, x, resultObj$ = {}; 1354 | for (k in object) { 1355 | x = object[k]; 1356 | if (!f(x)) { 1357 | resultObj$[k] = x; 1358 | } 1359 | } 1360 | return resultObj$; 1361 | }); 1362 | partition = curry$(function(f, object){ 1363 | var passed, failed, k, x; 1364 | passed = {}; 1365 | failed = {}; 1366 | for (k in object) { 1367 | x = object[k]; 1368 | (f(x) ? passed : failed)[k] = x; 1369 | } 1370 | return [passed, failed]; 1371 | }); 1372 | find = curry$(function(f, object){ 1373 | var i$, x; 1374 | for (i$ in object) { 1375 | x = object[i$]; 1376 | if (f(x)) { 1377 | return x; 1378 | } 1379 | } 1380 | }); 1381 | module.exports = { 1382 | values: values, 1383 | keys: keys, 1384 | pairsToObj: pairsToObj, 1385 | objToPairs: objToPairs, 1386 | listsToObj: listsToObj, 1387 | objToLists: objToLists, 1388 | empty: empty, 1389 | each: each, 1390 | map: map, 1391 | filter: filter, 1392 | compact: compact, 1393 | reject: reject, 1394 | partition: partition, 1395 | find: find 1396 | }; 1397 | function curry$(f, bound){ 1398 | var context, 1399 | _curry = function(args) { 1400 | return f.length > 1 ? function(){ 1401 | var params = args ? args.concat() : []; 1402 | context = bound ? context || this : this; 1403 | return params.push.apply(params, arguments) < 1404 | f.length && arguments.length ? 1405 | _curry.call(context, params) : f.apply(context, params); 1406 | } : f; 1407 | }; 1408 | return _curry(); 1409 | } 1410 | },{}],7:[function(require,module,exports){ 1411 | // Generated by LiveScript 1.6.0 1412 | var split, join, lines, unlines, words, unwords, chars, unchars, reverse, repeat, capitalize, camelize, dasherize; 1413 | split = curry$(function(sep, str){ 1414 | return str.split(sep); 1415 | }); 1416 | join = curry$(function(sep, xs){ 1417 | return xs.join(sep); 1418 | }); 1419 | lines = function(str){ 1420 | if (!str.length) { 1421 | return []; 1422 | } 1423 | return str.split('\n'); 1424 | }; 1425 | unlines = function(it){ 1426 | return it.join('\n'); 1427 | }; 1428 | words = function(str){ 1429 | if (!str.length) { 1430 | return []; 1431 | } 1432 | return str.split(/[ ]+/); 1433 | }; 1434 | unwords = function(it){ 1435 | return it.join(' '); 1436 | }; 1437 | chars = function(it){ 1438 | return it.split(''); 1439 | }; 1440 | unchars = function(it){ 1441 | return it.join(''); 1442 | }; 1443 | reverse = function(str){ 1444 | return str.split('').reverse().join(''); 1445 | }; 1446 | repeat = curry$(function(n, str){ 1447 | var result, i$; 1448 | result = ''; 1449 | for (i$ = 0; i$ < n; ++i$) { 1450 | result += str; 1451 | } 1452 | return result; 1453 | }); 1454 | capitalize = function(str){ 1455 | return str.charAt(0).toUpperCase() + str.slice(1); 1456 | }; 1457 | camelize = function(it){ 1458 | return it.replace(/[-_]+(.)?/g, function(arg$, c){ 1459 | return (c != null ? c : '').toUpperCase(); 1460 | }); 1461 | }; 1462 | dasherize = function(str){ 1463 | return str.replace(/([^-A-Z])([A-Z]+)/g, function(arg$, lower, upper){ 1464 | return lower + "-" + (upper.length > 1 1465 | ? upper 1466 | : upper.toLowerCase()); 1467 | }).replace(/^([A-Z]+)/, function(arg$, upper){ 1468 | if (upper.length > 1) { 1469 | return upper + "-"; 1470 | } else { 1471 | return upper.toLowerCase(); 1472 | } 1473 | }); 1474 | }; 1475 | module.exports = { 1476 | split: split, 1477 | join: join, 1478 | lines: lines, 1479 | unlines: unlines, 1480 | words: words, 1481 | unwords: unwords, 1482 | chars: chars, 1483 | unchars: unchars, 1484 | reverse: reverse, 1485 | repeat: repeat, 1486 | capitalize: capitalize, 1487 | camelize: camelize, 1488 | dasherize: dasherize 1489 | }; 1490 | function curry$(f, bound){ 1491 | var context, 1492 | _curry = function(args) { 1493 | return f.length > 1 ? function(){ 1494 | var params = args ? args.concat() : []; 1495 | context = bound ? context || this : this; 1496 | return params.push.apply(params, arguments) < 1497 | f.length && arguments.length ? 1498 | _curry.call(context, params) : f.apply(context, params); 1499 | } : f; 1500 | }; 1501 | return _curry(); 1502 | } 1503 | },{}],8:[function(require,module,exports){ 1504 | // Generated by LiveScript 1.6.0 1505 | var Func, List, Obj, Str, Num, id, isType, replicate, prelude, toString$ = {}.toString; 1506 | Func = require('./Func.js'); 1507 | List = require('./List.js'); 1508 | Obj = require('./Obj.js'); 1509 | Str = require('./Str.js'); 1510 | Num = require('./Num.js'); 1511 | id = function(x){ 1512 | return x; 1513 | }; 1514 | isType = curry$(function(type, x){ 1515 | return toString$.call(x).slice(8, -1) === type; 1516 | }); 1517 | replicate = curry$(function(n, x){ 1518 | var i$, results$ = []; 1519 | for (i$ = 0; i$ < n; ++i$) { 1520 | results$.push(x); 1521 | } 1522 | return results$; 1523 | }); 1524 | Str.empty = List.empty; 1525 | Str.slice = List.slice; 1526 | Str.take = List.take; 1527 | Str.drop = List.drop; 1528 | Str.splitAt = List.splitAt; 1529 | Str.takeWhile = List.takeWhile; 1530 | Str.dropWhile = List.dropWhile; 1531 | Str.span = List.span; 1532 | Str.breakStr = List.breakList; 1533 | prelude = { 1534 | Func: Func, 1535 | List: List, 1536 | Obj: Obj, 1537 | Str: Str, 1538 | Num: Num, 1539 | id: id, 1540 | isType: isType, 1541 | replicate: replicate 1542 | }; 1543 | prelude.each = List.each; 1544 | prelude.map = List.map; 1545 | prelude.filter = List.filter; 1546 | prelude.compact = List.compact; 1547 | prelude.reject = List.reject; 1548 | prelude.partition = List.partition; 1549 | prelude.find = List.find; 1550 | prelude.head = List.head; 1551 | prelude.first = List.first; 1552 | prelude.tail = List.tail; 1553 | prelude.last = List.last; 1554 | prelude.initial = List.initial; 1555 | prelude.empty = List.empty; 1556 | prelude.reverse = List.reverse; 1557 | prelude.difference = List.difference; 1558 | prelude.intersection = List.intersection; 1559 | prelude.union = List.union; 1560 | prelude.countBy = List.countBy; 1561 | prelude.groupBy = List.groupBy; 1562 | prelude.fold = List.fold; 1563 | prelude.foldl = List.foldl; 1564 | prelude.fold1 = List.fold1; 1565 | prelude.foldl1 = List.foldl1; 1566 | prelude.foldr = List.foldr; 1567 | prelude.foldr1 = List.foldr1; 1568 | prelude.unfoldr = List.unfoldr; 1569 | prelude.andList = List.andList; 1570 | prelude.orList = List.orList; 1571 | prelude.any = List.any; 1572 | prelude.all = List.all; 1573 | prelude.unique = List.unique; 1574 | prelude.uniqueBy = List.uniqueBy; 1575 | prelude.sort = List.sort; 1576 | prelude.sortWith = List.sortWith; 1577 | prelude.sortBy = List.sortBy; 1578 | prelude.sum = List.sum; 1579 | prelude.product = List.product; 1580 | prelude.mean = List.mean; 1581 | prelude.average = List.average; 1582 | prelude.concat = List.concat; 1583 | prelude.concatMap = List.concatMap; 1584 | prelude.flatten = List.flatten; 1585 | prelude.maximum = List.maximum; 1586 | prelude.minimum = List.minimum; 1587 | prelude.maximumBy = List.maximumBy; 1588 | prelude.minimumBy = List.minimumBy; 1589 | prelude.scan = List.scan; 1590 | prelude.scanl = List.scanl; 1591 | prelude.scan1 = List.scan1; 1592 | prelude.scanl1 = List.scanl1; 1593 | prelude.scanr = List.scanr; 1594 | prelude.scanr1 = List.scanr1; 1595 | prelude.slice = List.slice; 1596 | prelude.take = List.take; 1597 | prelude.drop = List.drop; 1598 | prelude.splitAt = List.splitAt; 1599 | prelude.takeWhile = List.takeWhile; 1600 | prelude.dropWhile = List.dropWhile; 1601 | prelude.span = List.span; 1602 | prelude.breakList = List.breakList; 1603 | prelude.zip = List.zip; 1604 | prelude.zipWith = List.zipWith; 1605 | prelude.zipAll = List.zipAll; 1606 | prelude.zipAllWith = List.zipAllWith; 1607 | prelude.at = List.at; 1608 | prelude.elemIndex = List.elemIndex; 1609 | prelude.elemIndices = List.elemIndices; 1610 | prelude.findIndex = List.findIndex; 1611 | prelude.findIndices = List.findIndices; 1612 | prelude.apply = Func.apply; 1613 | prelude.curry = Func.curry; 1614 | prelude.flip = Func.flip; 1615 | prelude.fix = Func.fix; 1616 | prelude.over = Func.over; 1617 | prelude.split = Str.split; 1618 | prelude.join = Str.join; 1619 | prelude.lines = Str.lines; 1620 | prelude.unlines = Str.unlines; 1621 | prelude.words = Str.words; 1622 | prelude.unwords = Str.unwords; 1623 | prelude.chars = Str.chars; 1624 | prelude.unchars = Str.unchars; 1625 | prelude.repeat = Str.repeat; 1626 | prelude.capitalize = Str.capitalize; 1627 | prelude.camelize = Str.camelize; 1628 | prelude.dasherize = Str.dasherize; 1629 | prelude.values = Obj.values; 1630 | prelude.keys = Obj.keys; 1631 | prelude.pairsToObj = Obj.pairsToObj; 1632 | prelude.objToPairs = Obj.objToPairs; 1633 | prelude.listsToObj = Obj.listsToObj; 1634 | prelude.objToLists = Obj.objToLists; 1635 | prelude.max = Num.max; 1636 | prelude.min = Num.min; 1637 | prelude.negate = Num.negate; 1638 | prelude.abs = Num.abs; 1639 | prelude.signum = Num.signum; 1640 | prelude.quot = Num.quot; 1641 | prelude.rem = Num.rem; 1642 | prelude.div = Num.div; 1643 | prelude.mod = Num.mod; 1644 | prelude.recip = Num.recip; 1645 | prelude.pi = Num.pi; 1646 | prelude.tau = Num.tau; 1647 | prelude.exp = Num.exp; 1648 | prelude.sqrt = Num.sqrt; 1649 | prelude.ln = Num.ln; 1650 | prelude.pow = Num.pow; 1651 | prelude.sin = Num.sin; 1652 | prelude.tan = Num.tan; 1653 | prelude.cos = Num.cos; 1654 | prelude.acos = Num.acos; 1655 | prelude.asin = Num.asin; 1656 | prelude.atan = Num.atan; 1657 | prelude.atan2 = Num.atan2; 1658 | prelude.truncate = Num.truncate; 1659 | prelude.round = Num.round; 1660 | prelude.ceiling = Num.ceiling; 1661 | prelude.floor = Num.floor; 1662 | prelude.isItNaN = Num.isItNaN; 1663 | prelude.even = Num.even; 1664 | prelude.odd = Num.odd; 1665 | prelude.gcd = Num.gcd; 1666 | prelude.lcm = Num.lcm; 1667 | prelude.VERSION = '1.2.1'; 1668 | module.exports = prelude; 1669 | function curry$(f, bound){ 1670 | var context, 1671 | _curry = function(args) { 1672 | return f.length > 1 ? function(){ 1673 | var params = args ? args.concat() : []; 1674 | context = bound ? context || this : this; 1675 | return params.push.apply(params, arguments) < 1676 | f.length && arguments.length ? 1677 | _curry.call(context, params) : f.apply(context, params); 1678 | } : f; 1679 | }; 1680 | return _curry(); 1681 | } 1682 | },{"./Func.js":3,"./List.js":4,"./Num.js":5,"./Obj.js":6,"./Str.js":7}],"type-check":[function(require,module,exports){ 1683 | // Generated by LiveScript 1.6.0 1684 | (function(){ 1685 | var VERSION, parseType, parsedTypeCheck, typeCheck; 1686 | VERSION = '0.4.0'; 1687 | parseType = require('./parse-type'); 1688 | parsedTypeCheck = require('./check'); 1689 | typeCheck = function(type, input, options){ 1690 | return parsedTypeCheck(parseType(type), input, options); 1691 | }; 1692 | module.exports = { 1693 | VERSION: VERSION, 1694 | typeCheck: typeCheck, 1695 | parsedTypeCheck: parsedTypeCheck, 1696 | parseType: parseType 1697 | }; 1698 | }).call(this); 1699 | 1700 | },{"./check":1,"./parse-type":2}]},{},[]); 1701 | --------------------------------------------------------------------------------