[Pkg-javascript-commits] [node-type-check] 02/13: Import Upstream version 0.3.2

Praveen Arimbrathodiyil praveen at moszumanska.debian.org
Wed Oct 12 18:01:16 UTC 2016


This is an automated email from the git hooks/post-receive script.

praveen pushed a commit to branch master
in repository node-type-check.

commit 4fd4735eb31ee535665c311ee7ceab21c5e1ac60
Author: Praveen Arimbrathodiyil <praveen at debian.org>
Date:   Tue Oct 11 16:43:28 2016 +0530

    Import Upstream version 0.3.2
---
 .gitignore            |    4 +
 .travis.yml           |    3 +
 LICENSE               |   22 +
 Makefile              |   52 ++
 README.md             |  210 +++++++
 browser/type-check.js | 1662 +++++++++++++++++++++++++++++++++++++++++++++++++
 lib/check.js          |  126 ++++
 lib/index.js          |   16 +
 lib/parse-type.js     |  196 ++++++
 package.json          |   40 ++
 package.json.ls       |   35 ++
 preroll               |    9 +
 src/check.ls          |   76 +++
 src/index.ls          |    8 +
 src/parse-type.ls     |  122 ++++
 test/check.ls         |  222 +++++++
 test/index.ls         |    6 +
 test/parse-type.ls    |  326 ++++++++++
 18 files changed, 3135 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..273d5bc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*.swp
+node_modules
+coverage
+*.t
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..baa0031
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+  - 0.8
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..525b118
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) George Zahariev
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..a7fc956
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,52 @@
+default: all
+
+SRC = $(shell find src -name "*.ls" -type f | sort)
+LIB = $(SRC:src/%.ls=lib/%.js)
+
+LS = node_modules/livescript
+LSC = node_modules/.bin/lsc
+BROWSERIFY = node_modules/.bin/browserify
+MOCHA = node_modules/.bin/mocha
+MOCHA2 = node_modules/.bin/_mocha
+ISTANBUL = node_modules/.bin/istanbul
+
+package.json: package.json.ls
+	$(LSC) --compile package.json.ls
+
+lib:
+	mkdir -p lib/
+
+lib/%.js: src/%.ls lib
+	$(LSC) --compile --output lib "$<"
+
+browser:
+	mkdir -p browser/
+
+browser/type-check.js: $(LIB) browser
+	{ ./preroll ; $(BROWSERIFY) -r ./lib/index.js:type-check ; } > browser/type-check.js
+
+.PHONY: build build-browser test coverage dev-install loc clean
+
+all: build
+
+build: $(LIB) package.json
+
+build-browser: browser/type-check.js
+
+test: build
+	$(MOCHA) --reporter dot --ui tdd --compilers ls:$(LS)
+
+coverage: build
+	$(ISTANBUL) cover $(MOCHA2) -- --reporter dot --ui tdd --compilers ls:$(LS)
+
+dev-install: package.json
+	npm install .
+
+loc:
+	wc -l $(SRC)
+
+clean:
+	rm -f package.json
+	rm -rf lib
+	rm -rf browser
+	rm -rf coverage
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ec92d59
--- /dev/null
+++ b/README.md
@@ -0,0 +1,210 @@
+# type-check [![Build Status](https://travis-ci.org/gkz/type-check.png?branch=master)](https://travis-ci.org/gkz/type-check)
+
+<a name="type-check" />
+
+`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.3.2. Check out the [demo](http://gkz.github.io/type-check/).
+
+For updates on `type-check`, [follow me on twitter](https://twitter.com/gkzahariev).
+
+    npm install type-check
+
+## Quick Examples
+
+```js
+// Basic types:
+var typeCheck = require('type-check').typeCheck;
+typeCheck('Number', 1);               // true
+typeCheck('Number', 'str');           // false
+typeCheck('Error', new Error);        // true
+typeCheck('Undefined', undefined);    // true
+
+// Comment
+typeCheck('count::Number', 1);        // true
+
+// One type OR another type:
+typeCheck('Number | String', 2);      // true
+typeCheck('Number | String', 'str');  // true
+
+// Wildcard, matches all types:
+typeCheck('*', 2) // true
+
+// Array, all elements of a single type:
+typeCheck('[Number]', [1, 2, 3]);                // true
+typeCheck('[Number]', [1, 'str', 3]);            // false
+
+// Tuples, or fixed length arrays with elements of different types:
+typeCheck('(String, Number)', ['str', 2]);       // true
+typeCheck('(String, Number)', ['str']);          // false
+typeCheck('(String, Number)', ['str', 2, 5]);    // false
+
+// Object properties:
+typeCheck('{x: Number, y: Boolean}', {x: 2, y: false});             // true
+typeCheck('{x: Number, y: Boolean}',       {x: 2});                 // false
+typeCheck('{x: Number, y: Maybe Boolean}', {x: 2});                 // true
+typeCheck('{x: Number, y: Boolean}',      {x: 2, y: false, z: 3});  // false
+typeCheck('{x: Number, y: Boolean, ...}', {x: 2, y: false, z: 3});  // true
+
+// A particular type AND object properties:
+typeCheck('RegExp{source: String, ...}', /re/i);          // true
+typeCheck('RegExp{source: String, ...}', {source: 're'}); // false
+
+// Custom types:
+var opt = {customTypes:
+  {Even: { typeOf: 'Number', validate: function(x) { return x % 2 === 0; }}}};
+typeCheck('Even', 2, opt); // true
+
+// Nested:
+var type = '{a: (String, [Number], {y: Array, ...}), b: Error{message: String, ...}}'
+typeCheck(type, {a: ['hi', [1, 2, 3], {y: [1, 'ms']}], b: new Error('oh no')}); // true
+```
+
+Check out the [type syntax format](#syntax) and [guide](#guide).
+
+## Usage
+
+`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.
+
+```js
+// typeCheck(type, input, options);
+typeCheck('Number', 2);               // true
+
+// parseType(type);
+var parsedType = parseType('Number'); // object
+
+// parsedTypeCheck(parsedType, input, options);
+parsedTypeCheck(parsedType, 2);       // true
+```
+
+### typeCheck(type, input, options)
+
+`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`.
+
+##### arguments
+* type - `String` - the type written in the [type format](#type-format) which to check against
+* input - `*` - any JavaScript value, which is to be checked against the type
+* options - `Maybe Object` - an optional parameter specifying additional options, currently the only available option is specifying [custom types](#custom-types)
+
+##### returns
+`Boolean` - whether the input matches the type
+
+##### example
+```js
+typeCheck('Number', 2); // true
+```
+
+### parseType(type)
+
+`parseType` parses string `type` written in the [type format](#type-format) into an object representing the parsed type.
+
+##### arguments
+* type - `String` - the type written in the [type format](#type-format) which to parse
+
+##### returns
+`Object` - an object in the parsed type format representing the parsed type
+
+##### example
+```js
+parseType('Number'); // [{type: 'Number'}]
+```
+### parsedTypeCheck(parsedType, input, options)
+
+`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.
+
+##### arguments
+* type - `Object` - the type in the parsed type format which to check against
+* input - `*` - any JavaScript value, which is to be checked against the type
+* options - `Maybe Object` - an optional parameter specifying additional options, currently the only available option is specifying [custom types](#custom-types)
+
+##### returns
+`Boolean` - whether the input matches the type
+
+##### example
+```js
+parsedTypeCheck([{type: 'Number'}], 2); // true
+var parsedType = parseType('String');
+parsedTypeCheck(parsedType, 'str');     // true
+```
+
+<a name="type-format" />
+## Type Format
+
+### Syntax
+
+White space is ignored. The root node is a __Types__.
+
+* __Identifier__ = `[\$\w]+` - a group of any lower or upper case letters, numbers, underscores, or dollar signs - eg. `String`
+* __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}`, `*`
+* __Types__ = optionally a comment (an `Indentifier` followed by a `::`), optionally the identifier `Maybe`, one or more `Type`, separated by `|` - eg. `Number`, `String | Date`, `Maybe Number`, `Maybe Boolean | String`
+* __Structure__ = `Fields`, or a `Tuple`, or an `Array` - eg. `{x: Number}`, `(String, Number)`, `[Date]`
+* __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, ...}`
+* __Field__ = an `Identifier`, followed by a colon `:`, followed by `Types` - eg. `x: Date | String`, `y: Boolean`
+* __Tuple__ = a `(`, followed by one or more `Types` separated by a comma `,` (trailing comma `,` is permitted), followed by a `)` - eg `(Date)`, `(Number, Date)`
+* __Array__ = a `[` followed by exactly one `Types` followed by a `]` - eg. `[Boolean]`, `[Boolean | Null]`
+
+### Guide
+
+`type-check` uses `Object.toString` to find out the basic type of a value. Specifically,
+
+```js
+{}.toString.call(VALUE).slice(8, -1)
+{}.toString.call(true).slice(8, -1) // 'Boolean'
+```
+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'`.
+
+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.
+
+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`.
+
+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.
+
+The wildcard `*` matches all types.
+
+There are three types of structures for checking the contents of a value: 'fields', 'tuple', and 'array'.
+
+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}`.
+
+To check if the properties pass, and the value is of a certain type, you can specify the type - eg. `Error{message: String}`.
+
+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).
+
+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.
+
+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]`.
+
+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)`.
+
+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.
+
+Check out the [type precedence](https://github.com/zaboco/type-precedence) library for type-check.
+
+## Options
+
+Options is an object. It is an optional parameter to the `typeCheck` and `parsedTypeCheck` functions. The only current option is `customTypes`.
+
+<a name="custom-types" />
+### Custom Types
+
+__Example:__
+
+```js
+var options = {
+  customTypes: {
+    Even: {
+      typeOf: 'Number',
+      validate: function(x) {
+        return x % 2 === 0;
+      }
+    }
+  }
+};
+typeCheck('Even', 2, options); // true
+typeCheck('Even', 3, options); // false
+```
+
+`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.
+
+The `typeOf` property is the type the value should be, 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.
+
+## Technical About
+
+`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.
diff --git a/browser/type-check.js b/browser/type-check.js
new file mode 100644
index 0000000..96e0254
--- /dev/null
+++ b/browser/type-check.js
@@ -0,0 +1,1662 @@
+// Generated by LiveScript 1.4.0
+
+// type-check 0.3.1
+// Copyright (c) George Zahariev
+// Released under the MIT License
+// https://raw.githubusercontent.com/gkz/type-check/master/LICENSE
+require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+// Generated by LiveScript 1.4.0
+(function(){
+  var ref$, any, all, isItNaN, types, defaultType, customTypes, toString$ = {}.toString;
+  ref$ = require('prelude-ls'), any = ref$.any, all = ref$.all, isItNaN = ref$.isItNaN;
+  types = {
+    Number: {
+      typeOf: 'Number',
+      validate: function(it){
+        return !isItNaN(it);
+      }
+    },
+    NaN: {
+      typeOf: 'Number',
+      validate: isItNaN
+    },
+    Int: {
+      typeOf: 'Number',
+      validate: function(it){
+        return !isItNaN(it) && it % 1 === 0;
+      }
+    },
+    Float: {
+      typeOf: 'Number',
+      validate: function(it){
+        return !isItNaN(it);
+      }
+    },
+    Date: {
+      typeOf: 'Date',
+      validate: function(it){
+        return !isItNaN(it.getTime());
+      }
+    }
+  };
+  defaultType = {
+    array: 'Array',
+    tuple: 'Array'
+  };
+  function checkArray(input, type){
+    return all(function(it){
+      return checkMultiple(it, type.of);
+    }, input);
+  }
+  function checkTuple(input, type){
+    var i, i$, ref$, len$, types;
+    i = 0;
+    for (i$ = 0, len$ = (ref$ = type.of).length; i$ < len$; ++i$) {
+      types = ref$[i$];
+      if (!checkMultiple(input[i], types)) {
+        return false;
+      }
+      i++;
+    }
+    return input.length <= i;
+  }
+  function checkFields(input, type){
+    var inputKeys, numInputKeys, k, numOfKeys, key, ref$, types;
+    inputKeys = {};
+    numInputKeys = 0;
+    for (k in input) {
+      inputKeys[k] = true;
+      numInputKeys++;
+    }
+    numOfKeys = 0;
+    for (key in ref$ = type.of) {
+      types = ref$[key];
+      if (!checkMultiple(input[key], types)) {
+        return false;
+      }
+      if (inputKeys[key]) {
+        numOfKeys++;
+      }
+    }
+    return type.subset || numInputKeys === numOfKeys;
+  }
+  function checkStructure(input, type){
+    if (!(input instanceof Object)) {
+      return false;
+    }
+    switch (type.structure) {
+    case 'fields':
+      return checkFields(input, type);
+    case 'array':
+      return checkArray(input, type);
+    case 'tuple':
+      return checkTuple(input, type);
+    }
+  }
+  function check(input, typeObj){
+    var type, structure, setting, that;
+    type = typeObj.type, structure = typeObj.structure;
+    if (type) {
+      if (type === '*') {
+        return true;
+      }
+      setting = customTypes[type] || types[type];
+      if (setting) {
+        return setting.typeOf === toString$.call(input).slice(8, -1) && setting.validate(input);
+      } else {
+        return type === toString$.call(input).slice(8, -1) && (!structure || checkStructure(input, typeObj));
+      }
+    } else if (structure) {
+      if (that = defaultType[structure]) {
+        if (that !== toString$.call(input).slice(8, -1)) {
+          return false;
+        }
+      }
+      return checkStructure(input, typeObj);
+    } else {
+      throw new Error("No type defined. Input: " + input + ".");
+    }
+  }
+  function checkMultiple(input, types){
+    if (toString$.call(types).slice(8, -1) !== 'Array') {
+      throw new Error("Types must be in an array. Input: " + input + ".");
+    }
+    return any(function(it){
+      return check(input, it);
+    }, types);
+  }
+  module.exports = function(parsedType, input, options){
+    options == null && (options = {});
+    customTypes = options.customTypes || {};
+    return checkMultiple(input, parsedType);
+  };
+}).call(this);
+
+},{"prelude-ls":8}],2:[function(require,module,exports){
+// Generated by LiveScript 1.4.0
+(function(){
+  var identifierRegex, tokenRegex;
+  identifierRegex = /[\$\w]+/;
+  function peek(tokens){
+    var token;
+    token = tokens[0];
+    if (token == null) {
+      throw new Error('Unexpected end of input.');
+    }
+    return token;
+  }
+  function consumeIdent(tokens){
+    var token;
+    token = peek(tokens);
+    if (!identifierRegex.test(token)) {
+      throw new Error("Expected text, got '" + token + "' instead.");
+    }
+    return tokens.shift();
+  }
+  function consumeOp(tokens, op){
+    var token;
+    token = peek(tokens);
+    if (token !== op) {
+      throw new Error("Expected '" + op + "', got '" + token + "' instead.");
+    }
+    return tokens.shift();
+  }
+  function maybeConsumeOp(tokens, op){
+    var token;
+    token = tokens[0];
+    if (token === op) {
+      return tokens.shift();
+    } else {
+      return null;
+    }
+  }
+  function consumeArray(tokens){
+    var types;
+    consumeOp(tokens, '[');
+    if (peek(tokens) === ']') {
+      throw new Error("Must specify type of Array - eg. [Type], got [] instead.");
+    }
+    types = consumeTypes(tokens);
+    consumeOp(tokens, ']');
+    return {
+      structure: 'array',
+      of: types
+    };
+  }
+  function consumeTuple(tokens){
+    var components;
+    components = [];
+    consumeOp(tokens, '(');
+    if (peek(tokens) === ')') {
+      throw new Error("Tuple must be of at least length 1 - eg. (Type), got () instead.");
+    }
+    for (;;) {
+      components.push(consumeTypes(tokens));
+      maybeConsumeOp(tokens, ',');
+      if (')' === peek(tokens)) {
+        break;
+      }
+    }
+    consumeOp(tokens, ')');
+    return {
+      structure: 'tuple',
+      of: components
+    };
+  }
+  function consumeFields(tokens){
+    var fields, subset, ref$, key, types;
+    fields = {};
+    consumeOp(tokens, '{');
+    subset = false;
+    for (;;) {
+      if (maybeConsumeOp(tokens, '...')) {
+        subset = true;
+        break;
+      }
+      ref$ = consumeField(tokens), key = ref$[0], types = ref$[1];
+      fields[key] = types;
+      maybeConsumeOp(tokens, ',');
+      if ('}' === peek(tokens)) {
+        break;
+      }
+    }
+    consumeOp(tokens, '}');
+    return {
+      structure: 'fields',
+      of: fields,
+      subset: subset
+    };
+  }
+  function consumeField(tokens){
+    var key, types;
+    key = consumeIdent(tokens);
+    consumeOp(tokens, ':');
+    types = consumeTypes(tokens);
+    return [key, types];
+  }
+  function maybeConsumeStructure(tokens){
+    switch (tokens[0]) {
+    case '[':
+      return consumeArray(tokens);
+    case '(':
+      return consumeTuple(tokens);
+    case '{':
+      return consumeFields(tokens);
+    }
+  }
+  function consumeType(tokens){
+    var token, wildcard, type, structure;
+    token = peek(tokens);
+    wildcard = token === '*';
+    if (wildcard || identifierRegex.test(token)) {
+      type = wildcard
+        ? consumeOp(tokens, '*')
+        : consumeIdent(tokens);
+      structure = maybeConsumeStructure(tokens);
+      if (structure) {
+        return structure.type = type, structure;
+      } else {
+        return {
+          type: type
+        };
+      }
+    } else {
+      structure = maybeConsumeStructure(tokens);
+      if (!structure) {
+        throw new Error("Unexpected character: " + token);
+      }
+      return structure;
+    }
+  }
+  function consumeTypes(tokens){
+    var lookahead, types, typesSoFar, typeObj, type;
+    if ('::' === peek(tokens)) {
+      throw new Error("No comment before comment separator '::' found.");
+    }
+    lookahead = tokens[1];
+    if (lookahead != null && lookahead === '::') {
+      tokens.shift();
+      tokens.shift();
+    }
+    types = [];
+    typesSoFar = {};
+    if ('Maybe' === peek(tokens)) {
+      tokens.shift();
+      types = [
+        {
+          type: 'Undefined'
+        }, {
+          type: 'Null'
+        }
+      ];
+      typesSoFar = {
+        Undefined: true,
+        Null: true
+      };
+    }
+    for (;;) {
+      typeObj = consumeType(tokens), type = typeObj.type;
+      if (!typesSoFar[type]) {
+        types.push(typeObj);
+      }
+      typesSoFar[type] = true;
+      if (!maybeConsumeOp(tokens, '|')) {
+        break;
+      }
+    }
+    return types;
+  }
+  tokenRegex = RegExp('\\.\\.\\.|::|->|' + identifierRegex.source + '|\\S', 'g');
+  module.exports = function(input){
+    var tokens, e;
+    if (!input.length) {
+      throw new Error('No type specified.');
+    }
+    tokens = input.match(tokenRegex) || [];
+    if (in$('->', tokens)) {
+      throw new Error("Function types are not supported.\ To validate that something is a function, you may use 'Function'.");
+    }
+    try {
+      return consumeTypes(tokens);
+    } catch (e$) {
+      e = e$;
+      throw new Error(e.message + " - Remaining tokens: " + JSON.stringify(tokens) + " - Initial input: '" + input + "'");
+    }
+  };
+  function in$(x, xs){
+    var i = -1, l = xs.length >>> 0;
+    while (++i < l) if (x === xs[i]) return true;
+    return false;
+  }
+}).call(this);
+
+},{}],3:[function(require,module,exports){
+// Generated by LiveScript 1.4.0
+var apply, curry, flip, fix, over, memoize, slice$ = [].slice, toString$ = {}.toString;
+apply = curry$(function(f, list){
+  return f.apply(null, list);
+});
+curry = function(f){
+  return curry$(f);
+};
+flip = curry$(function(f, x, y){
+  return f(y, x);
+});
+fix = function(f){
+  return function(g){
+    return function(){
+      return f(g(g)).apply(null, arguments);
+    };
+  }(function(g){
+    return function(){
+      return f(g(g)).apply(null, arguments);
+    };
+  });
+};
+over = curry$(function(f, g, x, y){
+  return f(g(x), g(y));
+});
+memoize = function(f){
+  var memo;
+  memo = {};
+  return function(){
+    var args, key, arg;
+    args = slice$.call(arguments);
+    key = (function(){
+      var i$, ref$, len$, results$ = [];
+      for (i$ = 0, len$ = (ref$ = args).length; i$ < len$; ++i$) {
+        arg = ref$[i$];
+        results$.push(arg + toString$.call(arg).slice(8, -1));
+      }
+      return results$;
+    }()).join('');
+    return memo[key] = key in memo
+      ? memo[key]
+      : f.apply(null, args);
+  };
+};
+module.exports = {
+  curry: curry,
+  flip: flip,
+  fix: fix,
+  apply: apply,
+  over: over,
+  memoize: memoize
+};
+function curry$(f, bound){
+  var context,
+  _curry = function(args) {
+    return f.length > 1 ? function(){
+      var params = args ? args.concat() : [];
+      context = bound ? context || this : this;
+      return params.push.apply(params, arguments) <
+          f.length && arguments.length ?
+        _curry.call(context, params) : f.apply(context, params);
+    } : f;
+  };
+  return _curry();
+}
+},{}],4:[function(require,module,exports){
+// Generated by LiveScript 1.4.0
+var each, map, compact, filter, reject, 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, z [...]
+each = curry$(function(f, xs){
+  var i$, len$, x;
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    f(x);
+  }
+  return xs;
+});
+map = curry$(function(f, xs){
+  var i$, len$, x, results$ = [];
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    results$.push(f(x));
+  }
+  return results$;
+});
+compact = function(xs){
+  var i$, len$, x, results$ = [];
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    if (x) {
+      results$.push(x);
+    }
+  }
+  return results$;
+};
+filter = curry$(function(f, xs){
+  var i$, len$, x, results$ = [];
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    if (f(x)) {
+      results$.push(x);
+    }
+  }
+  return results$;
+});
+reject = curry$(function(f, xs){
+  var i$, len$, x, results$ = [];
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    if (!f(x)) {
+      results$.push(x);
+    }
+  }
+  return results$;
+});
+partition = curry$(function(f, xs){
+  var passed, failed, i$, len$, x;
+  passed = [];
+  failed = [];
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    (f(x) ? passed : failed).push(x);
+  }
+  return [passed, failed];
+});
+find = curry$(function(f, xs){
+  var i$, len$, x;
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    if (f(x)) {
+      return x;
+    }
+  }
+});
+head = first = function(xs){
+  return xs[0];
+};
+tail = function(xs){
+  if (!xs.length) {
+    return;
+  }
+  return xs.slice(1);
+};
+last = function(xs){
+  return xs[xs.length - 1];
+};
+initial = function(xs){
+  if (!xs.length) {
+    return;
+  }
+  return xs.slice(0, -1);
+};
+empty = function(xs){
+  return !xs.length;
+};
+reverse = function(xs){
+  return xs.concat().reverse();
+};
+unique = function(xs){
+  var result, i$, len$, x;
+  result = [];
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    if (!in$(x, result)) {
+      result.push(x);
+    }
+  }
+  return result;
+};
+uniqueBy = curry$(function(f, xs){
+  var seen, i$, len$, x, val, results$ = [];
+  seen = [];
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    val = f(x);
+    if (in$(val, seen)) {
+      continue;
+    }
+    seen.push(val);
+    results$.push(x);
+  }
+  return results$;
+});
+fold = foldl = curry$(function(f, memo, xs){
+  var i$, len$, x;
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    memo = f(memo, x);
+  }
+  return memo;
+});
+fold1 = foldl1 = curry$(function(f, xs){
+  return fold(f, xs[0], xs.slice(1));
+});
+foldr = curry$(function(f, memo, xs){
+  var i$, x;
+  for (i$ = xs.length - 1; i$ >= 0; --i$) {
+    x = xs[i$];
+    memo = f(x, memo);
+  }
+  return memo;
+});
+foldr1 = curry$(function(f, xs){
+  return foldr(f, xs[xs.length - 1], xs.slice(0, -1));
+});
+unfoldr = curry$(function(f, b){
+  var result, x, that;
+  result = [];
+  x = b;
+  while ((that = f(x)) != null) {
+    result.push(that[0]);
+    x = that[1];
+  }
+  return result;
+});
+concat = function(xss){
+  return [].concat.apply([], xss);
+};
+concatMap = curry$(function(f, xs){
+  var x;
+  return [].concat.apply([], (function(){
+    var i$, ref$, len$, results$ = [];
+    for (i$ = 0, len$ = (ref$ = xs).length; i$ < len$; ++i$) {
+      x = ref$[i$];
+      results$.push(f(x));
+    }
+    return results$;
+  }()));
+});
+flatten = function(xs){
+  var x;
+  return [].concat.apply([], (function(){
+    var i$, ref$, len$, results$ = [];
+    for (i$ = 0, len$ = (ref$ = xs).length; i$ < len$; ++i$) {
+      x = ref$[i$];
+      if (toString$.call(x).slice(8, -1) === 'Array') {
+        results$.push(flatten(x));
+      } else {
+        results$.push(x);
+      }
+    }
+    return results$;
+  }()));
+};
+difference = function(xs){
+  var yss, results, i$, len$, x, j$, len1$, ys;
+  yss = slice$.call(arguments, 1);
+  results = [];
+  outer: for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    for (j$ = 0, len1$ = yss.length; j$ < len1$; ++j$) {
+      ys = yss[j$];
+      if (in$(x, ys)) {
+        continue outer;
+      }
+    }
+    results.push(x);
+  }
+  return results;
+};
+intersection = function(xs){
+  var yss, results, i$, len$, x, j$, len1$, ys;
+  yss = slice$.call(arguments, 1);
+  results = [];
+  outer: for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    for (j$ = 0, len1$ = yss.length; j$ < len1$; ++j$) {
+      ys = yss[j$];
+      if (!in$(x, ys)) {
+        continue outer;
+      }
+    }
+    results.push(x);
+  }
+  return results;
+};
+union = function(){
+  var xss, results, i$, len$, xs, j$, len1$, x;
+  xss = slice$.call(arguments);
+  results = [];
+  for (i$ = 0, len$ = xss.length; i$ < len$; ++i$) {
+    xs = xss[i$];
+    for (j$ = 0, len1$ = xs.length; j$ < len1$; ++j$) {
+      x = xs[j$];
+      if (!in$(x, results)) {
+        results.push(x);
+      }
+    }
+  }
+  return results;
+};
+countBy = curry$(function(f, xs){
+  var results, i$, len$, x, key;
+  results = {};
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    key = f(x);
+    if (key in results) {
+      results[key] += 1;
+    } else {
+      results[key] = 1;
+    }
+  }
+  return results;
+});
+groupBy = curry$(function(f, xs){
+  var results, i$, len$, x, key;
+  results = {};
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    key = f(x);
+    if (key in results) {
+      results[key].push(x);
+    } else {
+      results[key] = [x];
+    }
+  }
+  return results;
+});
+andList = function(xs){
+  var i$, len$, x;
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    if (!x) {
+      return false;
+    }
+  }
+  return true;
+};
+orList = function(xs){
+  var i$, len$, x;
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    if (x) {
+      return true;
+    }
+  }
+  return false;
+};
+any = curry$(function(f, xs){
+  var i$, len$, x;
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    if (f(x)) {
+      return true;
+    }
+  }
+  return false;
+});
+all = curry$(function(f, xs){
+  var i$, len$, x;
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    if (!f(x)) {
+      return false;
+    }
+  }
+  return true;
+});
+sort = function(xs){
+  return xs.concat().sort(function(x, y){
+    if (x > y) {
+      return 1;
+    } else if (x < y) {
+      return -1;
+    } else {
+      return 0;
+    }
+  });
+};
+sortWith = curry$(function(f, xs){
+  return xs.concat().sort(f);
+});
+sortBy = curry$(function(f, xs){
+  return xs.concat().sort(function(x, y){
+    if (f(x) > f(y)) {
+      return 1;
+    } else if (f(x) < f(y)) {
+      return -1;
+    } else {
+      return 0;
+    }
+  });
+});
+sum = function(xs){
+  var result, i$, len$, x;
+  result = 0;
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    result += x;
+  }
+  return result;
+};
+product = function(xs){
+  var result, i$, len$, x;
+  result = 1;
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    result *= x;
+  }
+  return result;
+};
+mean = average = function(xs){
+  var sum, i$, len$, x;
+  sum = 0;
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    x = xs[i$];
+    sum += x;
+  }
+  return sum / xs.length;
+};
+maximum = function(xs){
+  var max, i$, ref$, len$, x;
+  max = xs[0];
+  for (i$ = 0, len$ = (ref$ = xs.slice(1)).length; i$ < len$; ++i$) {
+    x = ref$[i$];
+    if (x > max) {
+      max = x;
+    }
+  }
+  return max;
+};
+minimum = function(xs){
+  var min, i$, ref$, len$, x;
+  min = xs[0];
+  for (i$ = 0, len$ = (ref$ = xs.slice(1)).length; i$ < len$; ++i$) {
+    x = ref$[i$];
+    if (x < min) {
+      min = x;
+    }
+  }
+  return min;
+};
+maximumBy = curry$(function(f, xs){
+  var max, i$, ref$, len$, x;
+  max = xs[0];
+  for (i$ = 0, len$ = (ref$ = xs.slice(1)).length; i$ < len$; ++i$) {
+    x = ref$[i$];
+    if (f(x) > f(max)) {
+      max = x;
+    }
+  }
+  return max;
+});
+minimumBy = curry$(function(f, xs){
+  var min, i$, ref$, len$, x;
+  min = xs[0];
+  for (i$ = 0, len$ = (ref$ = xs.slice(1)).length; i$ < len$; ++i$) {
+    x = ref$[i$];
+    if (f(x) < f(min)) {
+      min = x;
+    }
+  }
+  return min;
+});
+scan = scanl = curry$(function(f, memo, xs){
+  var last, x;
+  last = memo;
+  return [memo].concat((function(){
+    var i$, ref$, len$, results$ = [];
+    for (i$ = 0, len$ = (ref$ = xs).length; i$ < len$; ++i$) {
+      x = ref$[i$];
+      results$.push(last = f(last, x));
+    }
+    return results$;
+  }()));
+});
+scan1 = scanl1 = curry$(function(f, xs){
+  if (!xs.length) {
+    return;
+  }
+  return scan(f, xs[0], xs.slice(1));
+});
+scanr = curry$(function(f, memo, xs){
+  xs = xs.concat().reverse();
+  return scan(f, memo, xs).reverse();
+});
+scanr1 = curry$(function(f, xs){
+  if (!xs.length) {
+    return;
+  }
+  xs = xs.concat().reverse();
+  return scan(f, xs[0], xs.slice(1)).reverse();
+});
+slice = curry$(function(x, y, xs){
+  return xs.slice(x, y);
+});
+take = curry$(function(n, xs){
+  if (n <= 0) {
+    return xs.slice(0, 0);
+  } else {
+    return xs.slice(0, n);
+  }
+});
+drop = curry$(function(n, xs){
+  if (n <= 0) {
+    return xs;
+  } else {
+    return xs.slice(n);
+  }
+});
+splitAt = curry$(function(n, xs){
+  return [take(n, xs), drop(n, xs)];
+});
+takeWhile = curry$(function(p, xs){
+  var len, i;
+  len = xs.length;
+  if (!len) {
+    return xs;
+  }
+  i = 0;
+  while (i < len && p(xs[i])) {
+    i += 1;
+  }
+  return xs.slice(0, i);
+});
+dropWhile = curry$(function(p, xs){
+  var len, i;
+  len = xs.length;
+  if (!len) {
+    return xs;
+  }
+  i = 0;
+  while (i < len && p(xs[i])) {
+    i += 1;
+  }
+  return xs.slice(i);
+});
+span = curry$(function(p, xs){
+  return [takeWhile(p, xs), dropWhile(p, xs)];
+});
+breakList = curry$(function(p, xs){
+  return span(compose$(p, not$), xs);
+});
+zip = curry$(function(xs, ys){
+  var result, len, i$, len$, i, x;
+  result = [];
+  len = ys.length;
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    i = i$;
+    x = xs[i$];
+    if (i === len) {
+      break;
+    }
+    result.push([x, ys[i]]);
+  }
+  return result;
+});
+zipWith = curry$(function(f, xs, ys){
+  var result, len, i$, len$, i, x;
+  result = [];
+  len = ys.length;
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    i = i$;
+    x = xs[i$];
+    if (i === len) {
+      break;
+    }
+    result.push(f(x, ys[i]));
+  }
+  return result;
+});
+zipAll = function(){
+  var xss, minLength, i$, len$, xs, ref$, i, lresult$, j$, results$ = [];
+  xss = slice$.call(arguments);
+  minLength = undefined;
+  for (i$ = 0, len$ = xss.length; i$ < len$; ++i$) {
+    xs = xss[i$];
+    minLength <= (ref$ = xs.length) || (minLength = ref$);
+  }
+  for (i$ = 0; i$ < minLength; ++i$) {
+    i = i$;
+    lresult$ = [];
+    for (j$ = 0, len$ = xss.length; j$ < len$; ++j$) {
+      xs = xss[j$];
+      lresult$.push(xs[i]);
+    }
+    results$.push(lresult$);
+  }
+  return results$;
+};
+zipAllWith = function(f){
+  var xss, minLength, i$, len$, xs, ref$, i, results$ = [];
+  xss = slice$.call(arguments, 1);
+  minLength = undefined;
+  for (i$ = 0, len$ = xss.length; i$ < len$; ++i$) {
+    xs = xss[i$];
+    minLength <= (ref$ = xs.length) || (minLength = ref$);
+  }
+  for (i$ = 0; i$ < minLength; ++i$) {
+    i = i$;
+    results$.push(f.apply(null, (fn$())));
+  }
+  return results$;
+  function fn$(){
+    var i$, ref$, len$, results$ = [];
+    for (i$ = 0, len$ = (ref$ = xss).length; i$ < len$; ++i$) {
+      xs = ref$[i$];
+      results$.push(xs[i]);
+    }
+    return results$;
+  }
+};
+at = curry$(function(n, xs){
+  if (n < 0) {
+    return xs[xs.length + n];
+  } else {
+    return xs[n];
+  }
+});
+elemIndex = curry$(function(el, xs){
+  var i$, len$, i, x;
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    i = i$;
+    x = xs[i$];
+    if (x === el) {
+      return i;
+    }
+  }
+});
+elemIndices = curry$(function(el, xs){
+  var i$, len$, i, x, results$ = [];
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    i = i$;
+    x = xs[i$];
+    if (x === el) {
+      results$.push(i);
+    }
+  }
+  return results$;
+});
+findIndex = curry$(function(f, xs){
+  var i$, len$, i, x;
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    i = i$;
+    x = xs[i$];
+    if (f(x)) {
+      return i;
+    }
+  }
+});
+findIndices = curry$(function(f, xs){
+  var i$, len$, i, x, results$ = [];
+  for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) {
+    i = i$;
+    x = xs[i$];
+    if (f(x)) {
+      results$.push(i);
+    }
+  }
+  return results$;
+});
+module.exports = {
+  each: each,
+  map: map,
+  filter: filter,
+  compact: compact,
+  reject: reject,
+  partition: partition,
+  find: find,
+  head: head,
+  first: first,
+  tail: tail,
+  last: last,
+  initial: initial,
+  empty: empty,
+  reverse: reverse,
+  difference: difference,
+  intersection: intersection,
+  union: union,
+  countBy: countBy,
+  groupBy: groupBy,
+  fold: fold,
+  fold1: fold1,
+  foldl: foldl,
+  foldl1: foldl1,
+  foldr: foldr,
+  foldr1: foldr1,
+  unfoldr: unfoldr,
+  andList: andList,
+  orList: orList,
+  any: any,
+  all: all,
+  unique: unique,
+  uniqueBy: uniqueBy,
+  sort: sort,
+  sortWith: sortWith,
+  sortBy: sortBy,
+  sum: sum,
+  product: product,
+  mean: mean,
+  average: average,
+  concat: concat,
+  concatMap: concatMap,
+  flatten: flatten,
+  maximum: maximum,
+  minimum: minimum,
+  maximumBy: maximumBy,
+  minimumBy: minimumBy,
+  scan: scan,
+  scan1: scan1,
+  scanl: scanl,
+  scanl1: scanl1,
+  scanr: scanr,
+  scanr1: scanr1,
+  slice: slice,
+  take: take,
+  drop: drop,
+  splitAt: splitAt,
+  takeWhile: takeWhile,
+  dropWhile: dropWhile,
+  span: span,
+  breakList: breakList,
+  zip: zip,
+  zipWith: zipWith,
+  zipAll: zipAll,
+  zipAllWith: zipAllWith,
+  at: at,
+  elemIndex: elemIndex,
+  elemIndices: elemIndices,
+  findIndex: findIndex,
+  findIndices: findIndices
+};
+function curry$(f, bound){
+  var context,
+  _curry = function(args) {
+    return f.length > 1 ? function(){
+      var params = args ? args.concat() : [];
+      context = bound ? context || this : this;
+      return params.push.apply(params, arguments) <
+          f.length && arguments.length ?
+        _curry.call(context, params) : f.apply(context, params);
+    } : f;
+  };
+  return _curry();
+}
+function in$(x, xs){
+  var i = -1, l = xs.length >>> 0;
+  while (++i < l) if (x === xs[i]) return true;
+  return false;
+}
+function compose$() {
+  var functions = arguments;
+  return function() {
+    var i, result;
+    result = functions[0].apply(this, arguments);
+    for (i = 1; i < functions.length; ++i) {
+      result = functions[i](result);
+    }
+    return result;
+  };
+}
+function not$(x){ return !x; }
+},{}],5:[function(require,module,exports){
+// Generated by LiveScript 1.4.0
+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;
+max = curry$(function(x$, y$){
+  return x$ > y$ ? x$ : y$;
+});
+min = curry$(function(x$, y$){
+  return x$ < y$ ? x$ : y$;
+});
+negate = function(x){
+  return -x;
+};
+abs = Math.abs;
+signum = function(x){
+  if (x < 0) {
+    return -1;
+  } else if (x > 0) {
+    return 1;
+  } else {
+    return 0;
+  }
+};
+quot = curry$(function(x, y){
+  return ~~(x / y);
+});
+rem = curry$(function(x$, y$){
+  return x$ % y$;
+});
+div = curry$(function(x, y){
+  return Math.floor(x / y);
+});
+mod = curry$(function(x$, y$){
+  var ref$;
+  return (((x$) % (ref$ = y$) + ref$) % ref$);
+});
+recip = (function(it){
+  return 1 / it;
+});
+pi = Math.PI;
+tau = pi * 2;
+exp = Math.exp;
+sqrt = Math.sqrt;
+ln = Math.log;
+pow = curry$(function(x$, y$){
+  return Math.pow(x$, y$);
+});
+sin = Math.sin;
+tan = Math.tan;
+cos = Math.cos;
+asin = Math.asin;
+acos = Math.acos;
+atan = Math.atan;
+atan2 = curry$(function(x, y){
+  return Math.atan2(x, y);
+});
+truncate = function(x){
+  return ~~x;
+};
+round = Math.round;
+ceiling = Math.ceil;
+floor = Math.floor;
+isItNaN = function(x){
+  return x !== x;
+};
+even = function(x){
+  return x % 2 === 0;
+};
+odd = function(x){
+  return x % 2 !== 0;
+};
+gcd = curry$(function(x, y){
+  var z;
+  x = Math.abs(x);
+  y = Math.abs(y);
+  while (y !== 0) {
+    z = x % y;
+    x = y;
+    y = z;
+  }
+  return x;
+});
+lcm = curry$(function(x, y){
+  return Math.abs(Math.floor(x / gcd(x, y) * y));
+});
+module.exports = {
+  max: max,
+  min: min,
+  negate: negate,
+  abs: abs,
+  signum: signum,
+  quot: quot,
+  rem: rem,
+  div: div,
+  mod: mod,
+  recip: recip,
+  pi: pi,
+  tau: tau,
+  exp: exp,
+  sqrt: sqrt,
+  ln: ln,
+  pow: pow,
+  sin: sin,
+  tan: tan,
+  cos: cos,
+  acos: acos,
+  asin: asin,
+  atan: atan,
+  atan2: atan2,
+  truncate: truncate,
+  round: round,
+  ceiling: ceiling,
+  floor: floor,
+  isItNaN: isItNaN,
+  even: even,
+  odd: odd,
+  gcd: gcd,
+  lcm: lcm
+};
+function curry$(f, bound){
+  var context,
+  _curry = function(args) {
+    return f.length > 1 ? function(){
+      var params = args ? args.concat() : [];
+      context = bound ? context || this : this;
+      return params.push.apply(params, arguments) <
+          f.length && arguments.length ?
+        _curry.call(context, params) : f.apply(context, params);
+    } : f;
+  };
+  return _curry();
+}
+},{}],6:[function(require,module,exports){
+// Generated by LiveScript 1.4.0
+var values, keys, pairsToObj, objToPairs, listsToObj, objToLists, empty, each, map, compact, filter, reject, partition, find;
+values = function(object){
+  var i$, x, results$ = [];
+  for (i$ in object) {
+    x = object[i$];
+    results$.push(x);
+  }
+  return results$;
+};
+keys = function(object){
+  var x, results$ = [];
+  for (x in object) {
+    results$.push(x);
+  }
+  return results$;
+};
+pairsToObj = function(object){
+  var i$, len$, x, resultObj$ = {};
+  for (i$ = 0, len$ = object.length; i$ < len$; ++i$) {
+    x = object[i$];
+    resultObj$[x[0]] = x[1];
+  }
+  return resultObj$;
+};
+objToPairs = function(object){
+  var key, value, results$ = [];
+  for (key in object) {
+    value = object[key];
+    results$.push([key, value]);
+  }
+  return results$;
+};
+listsToObj = curry$(function(keys, values){
+  var i$, len$, i, key, resultObj$ = {};
+  for (i$ = 0, len$ = keys.length; i$ < len$; ++i$) {
+    i = i$;
+    key = keys[i$];
+    resultObj$[key] = values[i];
+  }
+  return resultObj$;
+});
+objToLists = function(object){
+  var keys, values, key, value;
+  keys = [];
+  values = [];
+  for (key in object) {
+    value = object[key];
+    keys.push(key);
+    values.push(value);
+  }
+  return [keys, values];
+};
+empty = function(object){
+  var x;
+  for (x in object) {
+    return false;
+  }
+  return true;
+};
+each = curry$(function(f, object){
+  var i$, x;
+  for (i$ in object) {
+    x = object[i$];
+    f(x);
+  }
+  return object;
+});
+map = curry$(function(f, object){
+  var k, x, resultObj$ = {};
+  for (k in object) {
+    x = object[k];
+    resultObj$[k] = f(x);
+  }
+  return resultObj$;
+});
+compact = function(object){
+  var k, x, resultObj$ = {};
+  for (k in object) {
+    x = object[k];
+    if (x) {
+      resultObj$[k] = x;
+    }
+  }
+  return resultObj$;
+};
+filter = curry$(function(f, object){
+  var k, x, resultObj$ = {};
+  for (k in object) {
+    x = object[k];
+    if (f(x)) {
+      resultObj$[k] = x;
+    }
+  }
+  return resultObj$;
+});
+reject = curry$(function(f, object){
+  var k, x, resultObj$ = {};
+  for (k in object) {
+    x = object[k];
+    if (!f(x)) {
+      resultObj$[k] = x;
+    }
+  }
+  return resultObj$;
+});
+partition = curry$(function(f, object){
+  var passed, failed, k, x;
+  passed = {};
+  failed = {};
+  for (k in object) {
+    x = object[k];
+    (f(x) ? passed : failed)[k] = x;
+  }
+  return [passed, failed];
+});
+find = curry$(function(f, object){
+  var i$, x;
+  for (i$ in object) {
+    x = object[i$];
+    if (f(x)) {
+      return x;
+    }
+  }
+});
+module.exports = {
+  values: values,
+  keys: keys,
+  pairsToObj: pairsToObj,
+  objToPairs: objToPairs,
+  listsToObj: listsToObj,
+  objToLists: objToLists,
+  empty: empty,
+  each: each,
+  map: map,
+  filter: filter,
+  compact: compact,
+  reject: reject,
+  partition: partition,
+  find: find
+};
+function curry$(f, bound){
+  var context,
+  _curry = function(args) {
+    return f.length > 1 ? function(){
+      var params = args ? args.concat() : [];
+      context = bound ? context || this : this;
+      return params.push.apply(params, arguments) <
+          f.length && arguments.length ?
+        _curry.call(context, params) : f.apply(context, params);
+    } : f;
+  };
+  return _curry();
+}
+},{}],7:[function(require,module,exports){
+// Generated by LiveScript 1.4.0
+var split, join, lines, unlines, words, unwords, chars, unchars, reverse, repeat, capitalize, camelize, dasherize;
+split = curry$(function(sep, str){
+  return str.split(sep);
+});
+join = curry$(function(sep, xs){
+  return xs.join(sep);
+});
+lines = function(str){
+  if (!str.length) {
+    return [];
+  }
+  return str.split('\n');
+};
+unlines = function(it){
+  return it.join('\n');
+};
+words = function(str){
+  if (!str.length) {
+    return [];
+  }
+  return str.split(/[ ]+/);
+};
+unwords = function(it){
+  return it.join(' ');
+};
+chars = function(it){
+  return it.split('');
+};
+unchars = function(it){
+  return it.join('');
+};
+reverse = function(str){
+  return str.split('').reverse().join('');
+};
+repeat = curry$(function(n, str){
+  var result, i$;
+  result = '';
+  for (i$ = 0; i$ < n; ++i$) {
+    result += str;
+  }
+  return result;
+});
+capitalize = function(str){
+  return str.charAt(0).toUpperCase() + str.slice(1);
+};
+camelize = function(it){
+  return it.replace(/[-_]+(.)?/g, function(arg$, c){
+    return (c != null ? c : '').toUpperCase();
+  });
+};
+dasherize = function(str){
+  return str.replace(/([^-A-Z])([A-Z]+)/g, function(arg$, lower, upper){
+    return lower + "-" + (upper.length > 1
+      ? upper
+      : upper.toLowerCase());
+  }).replace(/^([A-Z]+)/, function(arg$, upper){
+    if (upper.length > 1) {
+      return upper + "-";
+    } else {
+      return upper.toLowerCase();
+    }
+  });
+};
+module.exports = {
+  split: split,
+  join: join,
+  lines: lines,
+  unlines: unlines,
+  words: words,
+  unwords: unwords,
+  chars: chars,
+  unchars: unchars,
+  reverse: reverse,
+  repeat: repeat,
+  capitalize: capitalize,
+  camelize: camelize,
+  dasherize: dasherize
+};
+function curry$(f, bound){
+  var context,
+  _curry = function(args) {
+    return f.length > 1 ? function(){
+      var params = args ? args.concat() : [];
+      context = bound ? context || this : this;
+      return params.push.apply(params, arguments) <
+          f.length && arguments.length ?
+        _curry.call(context, params) : f.apply(context, params);
+    } : f;
+  };
+  return _curry();
+}
+},{}],8:[function(require,module,exports){
+// Generated by LiveScript 1.4.0
+var Func, List, Obj, Str, Num, id, isType, replicate, prelude, toString$ = {}.toString;
+Func = require('./Func.js');
+List = require('./List.js');
+Obj = require('./Obj.js');
+Str = require('./Str.js');
+Num = require('./Num.js');
+id = function(x){
+  return x;
+};
+isType = curry$(function(type, x){
+  return toString$.call(x).slice(8, -1) === type;
+});
+replicate = curry$(function(n, x){
+  var i$, results$ = [];
+  for (i$ = 0; i$ < n; ++i$) {
+    results$.push(x);
+  }
+  return results$;
+});
+Str.empty = List.empty;
+Str.slice = List.slice;
+Str.take = List.take;
+Str.drop = List.drop;
+Str.splitAt = List.splitAt;
+Str.takeWhile = List.takeWhile;
+Str.dropWhile = List.dropWhile;
+Str.span = List.span;
+Str.breakStr = List.breakList;
+prelude = {
+  Func: Func,
+  List: List,
+  Obj: Obj,
+  Str: Str,
+  Num: Num,
+  id: id,
+  isType: isType,
+  replicate: replicate
+};
+prelude.each = List.each;
+prelude.map = List.map;
+prelude.filter = List.filter;
+prelude.compact = List.compact;
+prelude.reject = List.reject;
+prelude.partition = List.partition;
+prelude.find = List.find;
+prelude.head = List.head;
+prelude.first = List.first;
+prelude.tail = List.tail;
+prelude.last = List.last;
+prelude.initial = List.initial;
+prelude.empty = List.empty;
+prelude.reverse = List.reverse;
+prelude.difference = List.difference;
+prelude.intersection = List.intersection;
+prelude.union = List.union;
+prelude.countBy = List.countBy;
+prelude.groupBy = List.groupBy;
+prelude.fold = List.fold;
+prelude.foldl = List.foldl;
+prelude.fold1 = List.fold1;
+prelude.foldl1 = List.foldl1;
+prelude.foldr = List.foldr;
+prelude.foldr1 = List.foldr1;
+prelude.unfoldr = List.unfoldr;
+prelude.andList = List.andList;
+prelude.orList = List.orList;
+prelude.any = List.any;
+prelude.all = List.all;
+prelude.unique = List.unique;
+prelude.uniqueBy = List.uniqueBy;
+prelude.sort = List.sort;
+prelude.sortWith = List.sortWith;
+prelude.sortBy = List.sortBy;
+prelude.sum = List.sum;
+prelude.product = List.product;
+prelude.mean = List.mean;
+prelude.average = List.average;
+prelude.concat = List.concat;
+prelude.concatMap = List.concatMap;
+prelude.flatten = List.flatten;
+prelude.maximum = List.maximum;
+prelude.minimum = List.minimum;
+prelude.maximumBy = List.maximumBy;
+prelude.minimumBy = List.minimumBy;
+prelude.scan = List.scan;
+prelude.scanl = List.scanl;
+prelude.scan1 = List.scan1;
+prelude.scanl1 = List.scanl1;
+prelude.scanr = List.scanr;
+prelude.scanr1 = List.scanr1;
+prelude.slice = List.slice;
+prelude.take = List.take;
+prelude.drop = List.drop;
+prelude.splitAt = List.splitAt;
+prelude.takeWhile = List.takeWhile;
+prelude.dropWhile = List.dropWhile;
+prelude.span = List.span;
+prelude.breakList = List.breakList;
+prelude.zip = List.zip;
+prelude.zipWith = List.zipWith;
+prelude.zipAll = List.zipAll;
+prelude.zipAllWith = List.zipAllWith;
+prelude.at = List.at;
+prelude.elemIndex = List.elemIndex;
+prelude.elemIndices = List.elemIndices;
+prelude.findIndex = List.findIndex;
+prelude.findIndices = List.findIndices;
+prelude.apply = Func.apply;
+prelude.curry = Func.curry;
+prelude.flip = Func.flip;
+prelude.fix = Func.fix;
+prelude.over = Func.over;
+prelude.split = Str.split;
+prelude.join = Str.join;
+prelude.lines = Str.lines;
+prelude.unlines = Str.unlines;
+prelude.words = Str.words;
+prelude.unwords = Str.unwords;
+prelude.chars = Str.chars;
+prelude.unchars = Str.unchars;
+prelude.repeat = Str.repeat;
+prelude.capitalize = Str.capitalize;
+prelude.camelize = Str.camelize;
+prelude.dasherize = Str.dasherize;
+prelude.values = Obj.values;
+prelude.keys = Obj.keys;
+prelude.pairsToObj = Obj.pairsToObj;
+prelude.objToPairs = Obj.objToPairs;
+prelude.listsToObj = Obj.listsToObj;
+prelude.objToLists = Obj.objToLists;
+prelude.max = Num.max;
+prelude.min = Num.min;
+prelude.negate = Num.negate;
+prelude.abs = Num.abs;
+prelude.signum = Num.signum;
+prelude.quot = Num.quot;
+prelude.rem = Num.rem;
+prelude.div = Num.div;
+prelude.mod = Num.mod;
+prelude.recip = Num.recip;
+prelude.pi = Num.pi;
+prelude.tau = Num.tau;
+prelude.exp = Num.exp;
+prelude.sqrt = Num.sqrt;
+prelude.ln = Num.ln;
+prelude.pow = Num.pow;
+prelude.sin = Num.sin;
+prelude.tan = Num.tan;
+prelude.cos = Num.cos;
+prelude.acos = Num.acos;
+prelude.asin = Num.asin;
+prelude.atan = Num.atan;
+prelude.atan2 = Num.atan2;
+prelude.truncate = Num.truncate;
+prelude.round = Num.round;
+prelude.ceiling = Num.ceiling;
+prelude.floor = Num.floor;
+prelude.isItNaN = Num.isItNaN;
+prelude.even = Num.even;
+prelude.odd = Num.odd;
+prelude.gcd = Num.gcd;
+prelude.lcm = Num.lcm;
+prelude.VERSION = '1.1.2';
+module.exports = prelude;
+function curry$(f, bound){
+  var context,
+  _curry = function(args) {
+    return f.length > 1 ? function(){
+      var params = args ? args.concat() : [];
+      context = bound ? context || this : this;
+      return params.push.apply(params, arguments) <
+          f.length && arguments.length ?
+        _curry.call(context, params) : f.apply(context, params);
+    } : f;
+  };
+  return _curry();
+}
+},{"./Func.js":3,"./List.js":4,"./Num.js":5,"./Obj.js":6,"./Str.js":7}],"type-check":[function(require,module,exports){
+// Generated by LiveScript 1.4.0
+(function(){
+  var VERSION, parseType, parsedTypeCheck, typeCheck;
+  VERSION = '0.3.1';
+  parseType = require('./parse-type');
+  parsedTypeCheck = require('./check');
+  typeCheck = function(type, input, options){
+    return parsedTypeCheck(parseType(type), input, options);
+  };
+  module.exports = {
+    VERSION: VERSION,
+    typeCheck: typeCheck,
+    parsedTypeCheck: parsedTypeCheck,
+    parseType: parseType
+  };
+}).call(this);
+
+},{"./check":1,"./parse-type":2}]},{},[]);
diff --git a/lib/check.js b/lib/check.js
new file mode 100644
index 0000000..0504c8d
--- /dev/null
+++ b/lib/check.js
@@ -0,0 +1,126 @@
+// Generated by LiveScript 1.4.0
+(function(){
+  var ref$, any, all, isItNaN, types, defaultType, customTypes, toString$ = {}.toString;
+  ref$ = require('prelude-ls'), any = ref$.any, all = ref$.all, isItNaN = ref$.isItNaN;
+  types = {
+    Number: {
+      typeOf: 'Number',
+      validate: function(it){
+        return !isItNaN(it);
+      }
+    },
+    NaN: {
+      typeOf: 'Number',
+      validate: isItNaN
+    },
+    Int: {
+      typeOf: 'Number',
+      validate: function(it){
+        return !isItNaN(it) && it % 1 === 0;
+      }
+    },
+    Float: {
+      typeOf: 'Number',
+      validate: function(it){
+        return !isItNaN(it);
+      }
+    },
+    Date: {
+      typeOf: 'Date',
+      validate: function(it){
+        return !isItNaN(it.getTime());
+      }
+    }
+  };
+  defaultType = {
+    array: 'Array',
+    tuple: 'Array'
+  };
+  function checkArray(input, type){
+    return all(function(it){
+      return checkMultiple(it, type.of);
+    }, input);
+  }
+  function checkTuple(input, type){
+    var i, i$, ref$, len$, types;
+    i = 0;
+    for (i$ = 0, len$ = (ref$ = type.of).length; i$ < len$; ++i$) {
+      types = ref$[i$];
+      if (!checkMultiple(input[i], types)) {
+        return false;
+      }
+      i++;
+    }
+    return input.length <= i;
+  }
+  function checkFields(input, type){
+    var inputKeys, numInputKeys, k, numOfKeys, key, ref$, types;
+    inputKeys = {};
+    numInputKeys = 0;
+    for (k in input) {
+      inputKeys[k] = true;
+      numInputKeys++;
+    }
+    numOfKeys = 0;
+    for (key in ref$ = type.of) {
+      types = ref$[key];
+      if (!checkMultiple(input[key], types)) {
+        return false;
+      }
+      if (inputKeys[key]) {
+        numOfKeys++;
+      }
+    }
+    return type.subset || numInputKeys === numOfKeys;
+  }
+  function checkStructure(input, type){
+    if (!(input instanceof Object)) {
+      return false;
+    }
+    switch (type.structure) {
+    case 'fields':
+      return checkFields(input, type);
+    case 'array':
+      return checkArray(input, type);
+    case 'tuple':
+      return checkTuple(input, type);
+    }
+  }
+  function check(input, typeObj){
+    var type, structure, setting, that;
+    type = typeObj.type, structure = typeObj.structure;
+    if (type) {
+      if (type === '*') {
+        return true;
+      }
+      setting = customTypes[type] || types[type];
+      if (setting) {
+        return setting.typeOf === toString$.call(input).slice(8, -1) && setting.validate(input);
+      } else {
+        return type === toString$.call(input).slice(8, -1) && (!structure || checkStructure(input, typeObj));
+      }
+    } else if (structure) {
+      if (that = defaultType[structure]) {
+        if (that !== toString$.call(input).slice(8, -1)) {
+          return false;
+        }
+      }
+      return checkStructure(input, typeObj);
+    } else {
+      throw new Error("No type defined. Input: " + input + ".");
+    }
+  }
+  function checkMultiple(input, types){
+    if (toString$.call(types).slice(8, -1) !== 'Array') {
+      throw new Error("Types must be in an array. Input: " + input + ".");
+    }
+    return any(function(it){
+      return check(input, it);
+    }, types);
+  }
+  module.exports = function(parsedType, input, options){
+    options == null && (options = {});
+    customTypes = options.customTypes || {};
+    return checkMultiple(input, parsedType);
+  };
+}).call(this);
diff --git a/lib/index.js b/lib/index.js
new file mode 100644
index 0000000..f3316ba
--- /dev/null
+++ b/lib/index.js
@@ -0,0 +1,16 @@
+// Generated by LiveScript 1.4.0
+(function(){
+  var VERSION, parseType, parsedTypeCheck, typeCheck;
+  VERSION = '0.3.2';
+  parseType = require('./parse-type');
+  parsedTypeCheck = require('./check');
+  typeCheck = function(type, input, options){
+    return parsedTypeCheck(parseType(type), input, options);
+  };
+  module.exports = {
+    VERSION: VERSION,
+    typeCheck: typeCheck,
+    parsedTypeCheck: parsedTypeCheck,
+    parseType: parseType
+  };
+}).call(this);
diff --git a/lib/parse-type.js b/lib/parse-type.js
new file mode 100644
index 0000000..5baf661
--- /dev/null
+++ b/lib/parse-type.js
@@ -0,0 +1,196 @@
+// Generated by LiveScript 1.4.0
+(function(){
+  var identifierRegex, tokenRegex;
+  identifierRegex = /[\$\w]+/;
+  function peek(tokens){
+    var token;
+    token = tokens[0];
+    if (token == null) {
+      throw new Error('Unexpected end of input.');
+    }
+    return token;
+  }
+  function consumeIdent(tokens){
+    var token;
+    token = peek(tokens);
+    if (!identifierRegex.test(token)) {
+      throw new Error("Expected text, got '" + token + "' instead.");
+    }
+    return tokens.shift();
+  }
+  function consumeOp(tokens, op){
+    var token;
+    token = peek(tokens);
+    if (token !== op) {
+      throw new Error("Expected '" + op + "', got '" + token + "' instead.");
+    }
+    return tokens.shift();
+  }
+  function maybeConsumeOp(tokens, op){
+    var token;
+    token = tokens[0];
+    if (token === op) {
+      return tokens.shift();
+    } else {
+      return null;
+    }
+  }
+  function consumeArray(tokens){
+    var types;
+    consumeOp(tokens, '[');
+    if (peek(tokens) === ']') {
+      throw new Error("Must specify type of Array - eg. [Type], got [] instead.");
+    }
+    types = consumeTypes(tokens);
+    consumeOp(tokens, ']');
+    return {
+      structure: 'array',
+      of: types
+    };
+  }
+  function consumeTuple(tokens){
+    var components;
+    components = [];
+    consumeOp(tokens, '(');
+    if (peek(tokens) === ')') {
+      throw new Error("Tuple must be of at least length 1 - eg. (Type), got () instead.");
+    }
+    for (;;) {
+      components.push(consumeTypes(tokens));
+      maybeConsumeOp(tokens, ',');
+      if (')' === peek(tokens)) {
+        break;
+      }
+    }
+    consumeOp(tokens, ')');
+    return {
+      structure: 'tuple',
+      of: components
+    };
+  }
+  function consumeFields(tokens){
+    var fields, subset, ref$, key, types;
+    fields = {};
+    consumeOp(tokens, '{');
+    subset = false;
+    for (;;) {
+      if (maybeConsumeOp(tokens, '...')) {
+        subset = true;
+        break;
+      }
+      ref$ = consumeField(tokens), key = ref$[0], types = ref$[1];
+      fields[key] = types;
+      maybeConsumeOp(tokens, ',');
+      if ('}' === peek(tokens)) {
+        break;
+      }
+    }
+    consumeOp(tokens, '}');
+    return {
+      structure: 'fields',
+      of: fields,
+      subset: subset
+    };
+  }
+  function consumeField(tokens){
+    var key, types;
+    key = consumeIdent(tokens);
+    consumeOp(tokens, ':');
+    types = consumeTypes(tokens);
+    return [key, types];
+  }
+  function maybeConsumeStructure(tokens){
+    switch (tokens[0]) {
+    case '[':
+      return consumeArray(tokens);
+    case '(':
+      return consumeTuple(tokens);
+    case '{':
+      return consumeFields(tokens);
+    }
+  }
+  function consumeType(tokens){
+    var token, wildcard, type, structure;
+    token = peek(tokens);
+    wildcard = token === '*';
+    if (wildcard || identifierRegex.test(token)) {
+      type = wildcard
+        ? consumeOp(tokens, '*')
+        : consumeIdent(tokens);
+      structure = maybeConsumeStructure(tokens);
+      if (structure) {
+        return structure.type = type, structure;
+      } else {
+        return {
+          type: type
+        };
+      }
+    } else {
+      structure = maybeConsumeStructure(tokens);
+      if (!structure) {
+        throw new Error("Unexpected character: " + token);
+      }
+      return structure;
+    }
+  }
+  function consumeTypes(tokens){
+    var lookahead, types, typesSoFar, typeObj, type;
+    if ('::' === peek(tokens)) {
+      throw new Error("No comment before comment separator '::' found.");
+    }
+    lookahead = tokens[1];
+    if (lookahead != null && lookahead === '::') {
+      tokens.shift();
+      tokens.shift();
+    }
+    types = [];
+    typesSoFar = {};
+    if ('Maybe' === peek(tokens)) {
+      tokens.shift();
+      types = [
+        {
+          type: 'Undefined'
+        }, {
+          type: 'Null'
+        }
+      ];
+      typesSoFar = {
+        Undefined: true,
+        Null: true
+      };
+    }
+    for (;;) {
+      typeObj = consumeType(tokens), type = typeObj.type;
+      if (!typesSoFar[type]) {
+        types.push(typeObj);
+      }
+      typesSoFar[type] = true;
+      if (!maybeConsumeOp(tokens, '|')) {
+        break;
+      }
+    }
+    return types;
+  }
+  tokenRegex = RegExp('\\.\\.\\.|::|->|' + identifierRegex.source + '|\\S', 'g');
+  module.exports = function(input){
+    var tokens, e;
+    if (!input.length) {
+      throw new Error('No type specified.');
+    }
+    tokens = input.match(tokenRegex) || [];
+    if (in$('->', tokens)) {
+      throw new Error("Function types are not supported.\ To validate that something is a function, you may use 'Function'.");
+    }
+    try {
+      return consumeTypes(tokens);
+    } catch (e$) {
+      e = e$;
+      throw new Error(e.message + " - Remaining tokens: " + JSON.stringify(tokens) + " - Initial input: '" + input + "'");
+    }
+  };
+  function in$(x, xs){
+    var i = -1, l = xs.length >>> 0;
+    while (++i < l) if (x === xs[i]) return true;
+    return false;
+  }
+}).call(this);
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..8c0b021
--- /dev/null
+++ b/package.json
@@ -0,0 +1,40 @@
+{
+  "name": "type-check",
+  "version": "0.3.2",
+  "author": "George Zahariev <z at georgezahariev.com>",
+  "description": "type-check allows you to check the types of JavaScript values at runtime with a Haskell like type syntax.",
+  "homepage": "https://github.com/gkz/type-check",
+  "keywords": [
+    "type",
+    "check",
+    "checking",
+    "library"
+  ],
+  "files": [
+    "lib",
+    "README.md",
+    "LICENSE"
+  ],
+  "main": "./lib/",
+  "bugs": "https://github.com/gkz/type-check/issues",
+  "license": "MIT",
+  "engines": {
+    "node": ">= 0.8.0"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/gkz/type-check.git"
+  },
+  "scripts": {
+    "test": "make test"
+  },
+  "dependencies": {
+    "prelude-ls": "~1.1.2"
+  },
+  "devDependencies": {
+    "livescript": "~1.4.0",
+    "mocha": "~2.3.4",
+    "istanbul": "~0.4.1",
+    "browserify": "~12.0.1"
+  }
+}
diff --git a/package.json.ls b/package.json.ls
new file mode 100644
index 0000000..38b16e8
--- /dev/null
+++ b/package.json.ls
@@ -0,0 +1,35 @@
+name: 'type-check'
+version: '0.3.2'
+
+author: 'George Zahariev <z at georgezahariev.com>'
+description: 'type-check allows you to check the types of JavaScript values at runtime with a Haskell like type syntax.'
+homepage: 'https://github.com/gkz/type-check'
+keywords:
+  'type'
+  'check'
+  'checking'
+  'library'
+files:
+  'lib'
+  'README.md'
+  'LICENSE'
+main: './lib/'
+
+bugs: 'https://github.com/gkz/type-check/issues'
+license: 'MIT'
+engines:
+  node: '>= 0.8.0'
+repository:
+  type: 'git'
+  url: 'git://github.com/gkz/type-check.git'
+scripts:
+  test: "make test"
+
+dependencies:
+  'prelude-ls': '~1.1.2'
+
+dev-dependencies:
+  livescript: '~1.4.0'
+  mocha: '~2.3.4'
+  istanbul: '~0.4.1'
+  browserify: '~12.0.1'
diff --git a/preroll b/preroll
new file mode 100755
index 0000000..4fa309e
--- /dev/null
+++ b/preroll
@@ -0,0 +1,9 @@
+#!/usr/bin/env node
+
+var version = require("./lib").VERSION;
+console.log("// Generated by LiveScript 1.4.0"
+          + "\n"
+          + "\n// type-check " + version
+          + "\n// Copyright (c) George Zahariev"
+          + "\n// Released under the MIT License"
+          + "\n// https://raw.githubusercontent.com/gkz/type-check/master/LICENSE");
diff --git a/src/check.ls b/src/check.ls
new file mode 100644
index 0000000..25c13e8
--- /dev/null
+++ b/src/check.ls
@@ -0,0 +1,76 @@
+{any, all, is-it-NaN} = require 'prelude-ls'
+
+types =
+  Number:
+    type-of: 'Number'
+    validate: -> not is-it-NaN it
+  NaN:
+    type-of: 'Number'
+    validate: is-it-NaN
+  Int:
+    type-of: 'Number'
+    validate: -> not is-it-NaN it and it % 1 is 0 # 1.0 is an Int
+  Float:
+    type-of: 'Number'
+    validate: -> not is-it-NaN it # same as number
+  Date:
+    type-of: 'Date'
+    validate: -> not is-it-NaN it.get-time! # make sure it isn't an invalid date
+
+default-type =
+  array: 'Array'
+  tuple: 'Array'
+
+function check-array input, type
+  all (-> check-multiple it, type.of), input
+
+function check-tuple input, type
+  i = 0
+  for types in type.of
+    return false unless check-multiple input[i], types
+    i++
+  input.length <= i # may be less if using 'Undefined' or 'Maybe' at the end
+
+function check-fields input, type
+  input-keys = {}
+  num-input-keys = 0
+  for k of input
+    input-keys[k] = true
+    num-input-keys++
+  num-of-keys = 0
+  for key, types of type.of
+    return false unless check-multiple input[key], types
+    num-of-keys++ if input-keys[key]
+  type.subset or num-input-keys is num-of-keys
+
+function check-structure input, type
+  return false if input not instanceof Object
+  switch type.structure
+  | 'fields' => check-fields input, type
+  | 'array'  => check-array input, type
+  | 'tuple'  => check-tuple input, type
+
+function check input, type-obj
+  {type, structure} = type-obj
+  if type
+    return true if type is '*' # wildcard
+    setting = custom-types[type] or types[type]
+    if setting
+      setting.type-of is typeof! input and setting.validate input
+    else
+      # Booleam, String, Null, Undefined, Error, user defined objects, etc.
+      type is typeof! input and (not structure or check-structure input, type-obj)
+  else if structure
+    return false unless that is typeof! input if default-type[structure]
+    check-structure input, type-obj
+  else
+    throw new Error "No type defined. Input: #input."
+
+function check-multiple input, types
+  throw new Error "Types must be in an array. Input: #input." unless typeof! types is 'Array'
+  any (-> check input, it), types
+
+var custom-types
+module.exports = (parsed-type, input, options = {}) ->
+  custom-types := options.custom-types or {}
+  check-multiple input, parsed-type
diff --git a/src/index.ls b/src/index.ls
new file mode 100644
index 0000000..d6927e7
--- /dev/null
+++ b/src/index.ls
@@ -0,0 +1,8 @@
+VERSION = '0.3.2'
+parse-type = require './parse-type'
+parsed-type-check = require './check'
+
+type-check = (type, input, options) ->
+  parsed-type-check (parse-type type), input, options
+
+module.exports = {VERSION, type-check, parsed-type-check, parse-type}
diff --git a/src/parse-type.ls b/src/parse-type.ls
new file mode 100644
index 0000000..443eb5c
--- /dev/null
+++ b/src/parse-type.ls
@@ -0,0 +1,122 @@
+# helpers
+identifier-regex = /[\$\w]+/
+
+function peek tokens # use instead of 'tokens.0' when it is required that the next token exists
+  token = tokens.0
+  throw new Error 'Unexpected end of input.' unless token?
+  token
+
+function consume-ident tokens
+  token = peek tokens
+  throw new Error "Expected text, got '#token' instead." unless identifier-regex.test token
+  tokens.shift!
+
+function consume-op tokens, op
+  token = peek tokens
+  throw new Error "Expected '#op', got '#token' instead." unless token is op
+  tokens.shift!
+
+function maybe-consume-op tokens, op
+  token = tokens.0
+  if token is op then tokens.shift! else null
+
+# structures
+function consume-array tokens
+  consume-op tokens, '['
+  throw new Error "Must specify type of Array - eg. [Type], got [] instead." if (peek tokens) is ']'
+  types = consume-types tokens
+  consume-op tokens, ']'
+  {structure: 'array', of: types}
+
+function consume-tuple tokens
+  components = []
+  consume-op tokens, '('
+  throw new Error "Tuple must be of at least length 1 - eg. (Type), got () instead." if (peek tokens) is ')'
+  for ever
+    components.push consume-types tokens
+    maybe-consume-op tokens, ','
+    break if ')' is peek tokens
+  consume-op tokens, ')'
+  {structure: 'tuple', of: components}
+
+function consume-fields tokens
+  fields = {}
+  consume-op tokens, '{'
+  subset = false
+  for ever
+    if maybe-consume-op tokens, '...'
+      subset := true
+      break
+    [key, types] = consume-field tokens
+    fields[key] = types
+    maybe-consume-op tokens, ','
+    break if '}' is peek tokens
+  consume-op tokens, '}'
+  {structure: 'fields', of: fields, subset}
+
+function consume-field tokens
+  key = consume-ident tokens
+  consume-op tokens, ':'
+  types = consume-types tokens
+  [key, types]
+
+# core
+function maybe-consume-structure tokens
+  switch tokens.0
+  | '[' => consume-array tokens
+  | '(' => consume-tuple tokens
+  | '{' => consume-fields tokens
+
+function consume-type tokens
+  token = peek tokens
+  wildcard = token is '*'
+  if wildcard or identifier-regex.test token
+    type = if wildcard then consume-op tokens, '*' else consume-ident tokens
+    structure = maybe-consume-structure tokens
+    if structure then structure <<< {type} else {type}
+  else
+    structure = maybe-consume-structure tokens
+    throw new Error "Unexpected character: #token" unless structure
+    structure
+
+function consume-types tokens
+  if '::' is peek tokens
+    throw new Error "No comment before comment separator '::' found."
+  lookahead = tokens.1
+  if lookahead? and lookahead is '::'
+    tokens.shift! # remove comment
+    tokens.shift! # remove ::
+  types = []
+  types-so-far = {} # for unique check
+  if 'Maybe' is peek tokens
+    tokens.shift!
+    types =
+      * type: 'Undefined'
+      * type: 'Null'
+    types-so-far = {+Undefined, +Null}
+  for ever
+    {type}:type-obj = consume-type tokens
+    types.push type-obj unless types-so-far[type]
+    types-so-far[type] = true
+    break unless maybe-consume-op tokens, '|'
+  types
+
+# single char ops used : , [ ] ( ) } { | *
+token-regex = //
+    \.\.\.                       # etc op
+  | ::                           # comment separator
+  | ->                           # arrow (for error generation purposes)
+  | #{ identifier-regex.source } # identifier
+  | \S                           # all single char ops - valid, and non-valid (for error purposes)
+  //g
+
+module.exports = (input) ->
+  throw new Error 'No type specified.' unless input.length
+  tokens = (input.match token-regex or [])
+  if '->' in tokens
+    throw new Error "Function types are not supported.
+                   \ To validate that something is a function, you may use 'Function'."
+  try
+    consume-types tokens
+  catch
+    throw new Error "#{e.message} - Remaining tokens: #{ JSON.stringify tokens } - Initial input: '#input'"
diff --git a/test/check.ls b/test/check.ls
new file mode 100644
index 0000000..ea62b1d
--- /dev/null
+++ b/test/check.ls
@@ -0,0 +1,222 @@
+{throws}:assert = require 'assert'
+{type-check: c, parsed-type-check} = require '..'
+
+suite 'check' ->
+  test 'Undefined' ->
+    assert c 'Undefined', void
+    assert c 'Undefined', Math.FAKE
+    assert not c 'Undefined', null
+    assert not c 'Undefined', false
+
+  test 'Undefined in field' ->
+    assert c '{a: Undefined}', {}
+    assert c '{a: Undefined}', {a: void}
+    assert not c '{a: Undefined}', {a: 1}
+
+  test 'Undefined in tuple' ->
+    assert c '(Undefined, Number)', [void, 2]
+    assert not c '(Undefined, Number)', [1, 2]
+
+  test 'Null' ->
+    assert c 'Null', null
+    assert not c 'Null', void
+    assert not c 'Null', false
+
+  test 'Boolean' ->
+    assert c 'Boolean', true
+    assert c 'Boolean', false
+    assert c 'Boolean', new Boolean false
+    assert not c 'Boolean', 1
+
+  test 'String' ->
+    assert c 'String', 'hi'
+    assert c 'String', new String 'hi'
+    assert not c 'String', 2
+
+  test 'Number' ->
+    assert c  'Number', 2
+    assert c  'Number', new Number 2
+    assert not c 'Number', 'hi'
+
+  test 'NaN' ->
+    assert c 'NaN', NaN
+    assert not c 'NaN', 1
+
+  test 'Int' ->
+    assert c 'Int', 1
+    assert c 'Int', 1.0
+    assert not c 'Int', 1.1
+
+  test 'Float' ->
+    assert c 'Float', 1
+    assert c 'Float', 1.0
+    assert c 'Float', 1.1
+
+  test 'Date' ->
+    assert c 'Date', new Date '2011-11-11'
+    assert not c 'Date', new Date '2011-1111'
+
+  test 'Function' ->
+    assert c 'Function', ->
+
+  test 'wildcard' ->
+    assert c '*', void
+    assert c '*', null
+    assert c '*', 2
+    assert c '*', {}
+    assert c '*', new Error
+    assert c '[*]', [1, null, void, 'hi', {x: 22}]
+
+  test 'multiple' ->
+    assert c 'Number | String', 'hi'
+    assert not c 'Date | Number', 'hi'
+
+  suite 'Array' ->
+    test 'bare' ->
+      assert c 'Array', [1, 2, 3]
+      assert c 'Array', [1, 'hi']
+      assert not c 'Array', true
+
+    test 'simple' ->
+      assert c '[Number]', [1, 2, 3]
+
+    test 'incorrect type' ->
+      assert not c '[Number]', true
+
+    test 'incorrect element type' ->
+      assert not c '[Number]', [1, 'hi']
+
+  suite 'Tuple' ->
+    test 'simple' ->
+      assert c '(String, Number)', ['hi', 2]
+
+    test 'too long' ->
+      assert not c '(String, Number)', ['hi', 2, 1]
+
+    test 'too short' ->
+      assert not c '(String, Number)', ['hi']
+
+    test 'incorrect type' ->
+      assert not c '(String, Number)', {}
+
+    test 'incorrect element type' ->
+      assert not c '(String, Number)', ['hi', 'bye']
+
+  test 'bare Object' ->
+    assert c 'Object', {}
+    assert c 'Object', {a: 1, length: 1}
+    assert not c 'Object', new Date
+
+  suite 'Maybe' ->
+    test 'simple' ->
+      assert c 'Maybe Number', 1
+      assert c 'Maybe Number', null
+      assert not c 'Maybe Number', 'string'
+
+    test 'with multiple' ->
+      type = 'Maybe Number | String'
+      assert c type, 2
+      assert c type, null
+      assert c type, 'hi'
+
+    test 'in fields' ->
+      type = '{a: Maybe String}'
+      assert c type, {a: 'string'}
+      assert c type, {a: null}
+      assert c type, {}
+      assert not c type, {a: 2}
+
+    test 'in tuple' ->
+      type = '(Number, Maybe String)'
+      assert c type, [1, 'hi']
+      assert c type, [1, null]
+      assert c type, [1]
+      assert not c '(Maybe String, Number)', [2]
+
+    test 'in array' ->
+      assert c '[Maybe Number]', [1, null, 2, void, 23]
+      assert c 'Object[Maybe String]', {0: 'a', 2: null, 5: 'b', length: 6}
+
+  suite 'duck typing' ->
+    test 'basic' ->
+      assert c '{a: String}', {a: 'hi'}
+
+    test 'property must by appropriate type' ->
+      assert not c '{a: String}', {a: 2}
+
+    test 'key must be the same' ->
+      assert not c '{a: String}', {b: 'hi'}
+
+    test 'not an object - fails' ->
+      assert not c '{a: String}', 2
+
+    test 'non-enumerable properties' ->
+      assert c '{parse: Function, stringify: Function}', JSON
+
+    test 'enumerable and non-enumerable properties' ->
+      assert c '{0: Number, 1: Number, length: Number}', [1, 2]
+
+    test 'using spread operator to check only a subset of the properties' ->
+      assert c '{length: Number, ...}', [1, 2]
+
+  suite 'structures with types' ->
+    test 'fields with Object' ->
+      assert c 'Object{a: String}', {a: 'hi'}
+      assert not c 'Object{a: String}', {a: 2}
+
+    test 'fields with Array' ->
+      assert c 'Array{0:Number, 1:Number, 2:Number}', [1, 2, 3]
+      assert c 'Array{0:Number, 1:Number, 2:Number}', [1, 2, 3]
+      assert c 'Array{0:Number, 1:String}', [1, 'hi']
+      assert not c 'Array{0:Number, 1:String}', [1]
+
+    test 'fields with JSON' ->
+      assert c 'JSON{parse: Function, stringify: Function}', JSON
+      assert not c 'JSON{parse: Function, stringify: Function}', {parse: ->, stringify: ->}
+
+    test 'fields with Math (using subset)' ->
+      assert c 'Math{PI: Float, sqrt: Function, ...}', Math
+
+    test 'array structure with Array' ->
+      assert c 'Array[Number]', [1, 2]
+
+    test 'array structure with Object' ->
+      assert c 'Object[Number]', {0: 1, 1: 2, length: 2}
+
+    test 'tuple structure with Array' ->
+      assert c 'Array(Number, String)', [1, 'two']
+
+    test 'tuple structure with Object' ->
+      assert c 'Object(Number, String)', {0: 1, 1: 'two', length: 2}
+
+  suite 'custom types' ->
+    test 'simple' ->
+      o =
+        custom-types:
+          Even:
+            type-of: 'Number'
+            validate: -> it % 2 is 0
+      assert c 'Even', 2, o
+      assert not c 'Even', 1, o
+
+    test 'overwrite current' ->
+      o =
+        custom-types:
+          Undefined:
+            type-of: 'String'
+            validate: -> it is 'bananas'
+
+      assert c 'Undefined', 'bananas', o
+      assert not c 'Undefined', void, o
+
+  test 'nested' ->
+    type = '{a: (String, [Number], {x: {a: Maybe Number}, y: Array, ...}), b: Error{message: String, ...}}'
+    assert c type, {a: ['hi', [1, 2, 3], {x: {a: 42}, y: [1, 'bye']}], b: new Error 'message'}
+    assert c type, {a: ['moo', [3], {x: {}, y: [], z: 999}], b: new Error '23'}
+
+  suite 'errors' ->
+    test 'no type defined' ->
+      throws (-> parsed-type-check [{}], true), /No type defined\. Input: true/
+
+    test 'types must be in array' ->
+      throws (-> parsed-type-check {}, true), /Types must be in an array\. Input: true/
diff --git a/test/index.ls b/test/index.ls
new file mode 100644
index 0000000..365eefb
--- /dev/null
+++ b/test/index.ls
@@ -0,0 +1,6 @@
+type-check = require '..'
+{strict-equal: equal} = require 'assert'
+
+suite 'index' ->
+  test 'version' ->
+    equal type-check.VERSION, (require '../package.json').version
diff --git a/test/parse-type.ls b/test/parse-type.ls
new file mode 100644
index 0000000..dbe7c70
--- /dev/null
+++ b/test/parse-type.ls
@@ -0,0 +1,326 @@
+{deep-equal, throws}:assert = require 'assert'
+{parse-type: p} = require '..'
+
+suite 'parse type' ->
+  test 'simple' ->
+    deep-equal (p 'Number'), [type: 'Number']
+
+  test 'different characters' ->
+    deep-equal (p '2T_and$'), [type: '2T_and$']
+
+  test 'Maybe' ->
+    deep-equal (p 'Maybe Number'), [
+      * type: 'Undefined'
+      * type: 'Null'
+      * type: 'Number'
+    ]
+    deep-equal (p 'Maybe Null | Number'), [
+      * type: 'Undefined'
+      * type: 'Null'
+      * type: 'Number'
+    ]
+    deep-equal (p 'Maybe Undefined | String'), [
+      * type: 'Undefined'
+      * type: 'Null'
+      * type: 'String'
+    ]
+
+  test 'wildcard' ->
+    deep-equal (p '*'), [type: '*']
+    deep-equal (p '[*]'), [
+      structure: 'array'
+      of: [type: '*']
+    ]
+    deep-equal (p '{x: *}'), [
+      structure: 'fields'
+      of:
+        x: [type: '*']
+      subset: false
+    ]
+    deep-equal (p '*{a:Number}'), [
+      type: '*'
+      structure: 'fields'
+      of:
+        a: [type: 'Number']
+      subset: false
+    ]
+
+  suite 'multiple types' ->
+    test 'one' ->
+      deep-equal (p 'Number'), [type: 'Number']
+
+    test 'two' ->
+      deep-equal (p 'Number | String'), [
+        * type: 'Number'
+        * type: 'String'
+      ]
+
+    test 'three' ->
+      deep-equal (p 'Number | String | Float'), [
+        * type: 'Number'
+        * type: 'String'
+        * type: 'Float'
+      ]
+
+    test 'two' ->
+      deep-equal (p 'Number | Number'), [
+        * type: 'Number'
+      ]
+
+  suite 'optional comment' ->
+    test 'basic' ->
+      deep-equal (p 'x :: Number'), [
+        * type: 'Number'
+      ]
+
+    test 'multiple' ->
+      deep-equal (p 'x :: Number | String'), [
+        * type: 'Number'
+        * type: 'String'
+      ]
+
+    test 'structures' ->
+      deep-equal (p 'list :: [Number]'), [
+        structure: 'array'
+        of: [type: 'Number']
+      ]
+      deep-equal (p '[element :: Number]'), [
+        structure: 'array'
+        of: [type: 'Number']
+      ]
+
+    test 'no comment specified' ->
+      throws (-> p ':: Number'), /No comment before comment separator '::' found/
+
+  suite 'array structure' ->
+    test 'simple' ->
+      deep-equal (p '[Number]'), [
+        structure: 'array'
+        of: [type: 'Number']
+      ]
+
+    test 'nested' ->
+      deep-equal (p '[[Number]]'), [
+        structure: 'array'
+        of: [
+          structure: 'array'
+          of: [type: 'Number']
+        ]
+      ]
+
+  suite 'array structure with type' ->
+    test 'simple' ->
+      deep-equal (p 'Int16Array[Int]'), [
+        type: 'Int16Array'
+        structure: 'array'
+        of: [type: 'Int']
+      ]
+
+    test 'nested' ->
+      deep-equal (p 'Array[Float32Array[Float]]'), [
+        type: 'Array'
+        structure: 'array'
+        of: [
+          type: 'Float32Array'
+          structure: 'array'
+          of: [type: 'Float']
+        ]
+      ]
+
+  suite 'tuple structure' ->
+    test 'single' ->
+      deep-equal (p '(Number)'), [
+        structure: 'tuple'
+        of: [
+          [type: 'Number']
+        ]
+      ]
+
+    test 'double' ->
+      deep-equal (p '(Number, String)'), [
+        structure: 'tuple'
+        of: [
+          [type: 'Number']
+          [type: 'String']
+        ]
+      ]
+
+    test 'trailing comma' ->
+      deep-equal (p '(Number, String,)'), [
+        structure: 'tuple'
+        of: [
+          [type: 'Number']
+          [type: 'String']
+        ]
+      ]
+
+    test 'nested' ->
+      deep-equal (p '((Number, String), (Float))'), [
+        structure: 'tuple'
+        of:
+          * [{
+              structure: 'tuple'
+              of:
+                * [type: 'Number']
+                * [type: 'String']
+            }]
+          * [{
+              structure: 'tuple'
+              of: [[type: 'Float']]
+            }]
+      ]
+
+  suite 'tuple structure with type' ->
+    test 'double' ->
+      deep-equal (p 'Type(Number, String)'), [
+        type: 'Type'
+        structure: 'tuple'
+        of: [
+          [type: 'Number']
+          [type: 'String']
+        ]
+      ]
+
+    test 'nested' ->
+      deep-equal (p 'Type(Type2(Number, String), Type3(Float))'), [
+        type: 'Type'
+        structure: 'tuple'
+        of:
+          * [{
+              type: 'Type2'
+              structure: 'tuple'
+              of:
+                * [type: 'Number']
+                * [type: 'String']
+            }]
+          * [{
+              type: 'Type3'
+              structure: 'tuple'
+              of: [[type: 'Float']]
+            }]
+      ]
+
+
+  suite 'fields structure, without type' ->
+    test 'simple' ->
+      deep-equal (p '{a:Number, b:String}'), [
+        structure: 'fields'
+        of:
+          a: [type: 'Number']
+          b: [type: 'String']
+        subset: false
+      ]
+
+    test 'trailing comma' ->
+      deep-equal (p '{a:Number, b:String,}'), [
+        structure: 'fields'
+        of:
+          a: [type: 'Number']
+          b: [type: 'String']
+        subset: false
+      ]
+
+    test 'nested' ->
+      deep-equal (p '{a: {message: String}, b:String}'), [
+        structure: 'fields'
+        of:
+          a: [{
+            structure: 'fields'
+            of:
+              message: [type: 'String']
+            subset: false
+          }]
+          b: [type: 'String']
+        subset: false
+      ]
+
+    test 'subset' ->
+      deep-equal (p '{a:Number, ...}'), [
+        structure: 'fields'
+        of:
+          a: [type: 'Number']
+        subset: true
+      ]
+
+    test 'no fields specified' ->
+      deep-equal (p '{...}'), [
+        structure: 'fields'
+        of: {}
+        subset: true
+      ]
+
+  suite 'fields structure, with type' ->
+    test 'simple' ->
+      deep-equal (p 'Object{a:Number, b:String}'), [
+        type: 'Object'
+        structure: 'fields'
+        of:
+          a: [type: 'Number']
+          b: [type: 'String']
+        subset: false
+      ]
+
+    test 'nested' ->
+      deep-equal (p 'Object{a: Error{message: String}, b:String}'), [
+        type: 'Object'
+        structure: 'fields'
+        of:
+          a: [{
+            type: 'Error'
+            structure: 'fields'
+            of:
+              message: [type: 'String']
+            subset: false
+          }]
+          b: [type: 'String']
+        subset: false
+      ]
+
+    test 'subset' ->
+      deep-equal (p 'Node{a:Number, ...}'), [
+        type: 'Node'
+        structure: 'fields'
+        of:
+          a: [type: 'Number']
+        subset: true
+      ]
+
+    test 'no fields specified' ->
+      deep-equal (p 'Date{...}'), [
+        type: 'Date'
+        structure: 'fields'
+        of: {}
+        subset: true
+      ]
+
+  suite 'errors' ->
+    test 'no type specified' ->
+      throws (-> p ''), /No type specified/
+
+    test 'tuple of length 0' ->
+      throws (-> p '()'), /Tuple must be of at least length 1/
+
+    test 'array without type' ->
+      throws (-> p '[]'), /Must specify type of Array/
+
+    test 'unexpected end of input' ->
+      throws (-> p ' '), /Unexpected end of input/
+      throws (-> p '['), /Unexpected end of input/
+      throws (-> p '[Number'), /Unexpected end of input/
+      throws (-> p '{'), /Unexpected end of input/
+
+    test 'unexpected end of input (input never tokenized)' ->
+      throws (-> p '{Number:'), /Unexpected end of input/
+
+    test 'unexpected character' ->
+      throws (-> p '[)'), /Unexpected character: \)/
+      throws (-> p '^'), /Unexpected character: \^/
+
+    test 'function types not supported' ->
+      throws (-> p 'Number -> String'), /Function types are not supported. To validate that something is a function, you may use 'Function'/
+
+    test 'expected op' ->
+      throws (-> p '[Number)'), /Expected '\]', got '\)' instead/
+
+    test 'expected text' ->
+      throws (-> p '{:Number}'), /Expected text, got ':' instead/

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-type-check.git



More information about the Pkg-javascript-commits mailing list