// Generated by LiveScript 1.2.0 (function(){

var ref$, id, reject, parsedTypeCheck, types, tokenRegex, toString$ = {}.toString;
ref$ = require('prelude-ls'), id = ref$.id, reject = ref$.reject;
parsedTypeCheck = require('type-check').parsedTypeCheck;
types = {
  '*': function(it){
    switch (toString$.call(it).slice(8, -1)) {
    case 'Array':
      return coerceType(it, {
        type: 'Array'
      });
    case 'Object':
      return coerceType(it, {
        type: 'Object'
      });
    default:
      return {
        type: 'Just',
        value: coerceTypes(it, [
          {
            type: 'Undefined'
          }, {
            type: 'Null'
          }, {
            type: 'Boolean'
          }, {
            type: 'Number'
          }, {
            type: 'Date'
          }, {
            type: 'RegExp'
          }, {
            type: 'Array'
          }, {
            type: 'Object'
          }, {
            type: 'String'
          }
        ], {
          explicit: true
        })
      };
    }
  },
  Undefined: function(it){
    if (it === 'undefined') {
      return {
        type: 'Just',
        value: void 8
      };
    } else {
      return {
        type: 'Nothing'
      };
    }
  },
  Null: function(it){
    if (it === 'null') {
      return {
        type: 'Just',
        value: null
      };
    } else {
      return {
        type: 'Nothing'
      };
    }
  },
  Boolean: function(it){
    if (it === 'true') {
      return {
        type: 'Just',
        value: true
      };
    } else if (it === 'false') {
      return {
        type: 'Just',
        value: false
      };
    } else {
      return {
        type: 'Nothing'
      };
    }
  },
  Number: function(it){
    return {
      type: 'Just',
      value: +it
    };
  },
  Int: function(it){
    return {
      type: 'Just',
      value: parseInt(it)
    };
  },
  Float: function(it){
    return {
      type: 'Just',
      value: parseFloat(it)
    };
  },
  Date: function(value, options){
    var that;
    if (that = /^\#(.*)\#$/.exec(value)) {
      return {
        type: 'Just',
        value: new Date(+that[1] || that[1])
      };
    } else if (options.explicit) {
      return {
        type: 'Nothing'
      };
    } else {
      return {
        type: 'Just',
        value: new Date(+value || value)
      };
    }
  },
  RegExp: function(value, options){
    var that;
    if (that = /^\/(.*)\/([gimy]*)$/.exec(value)) {
      return {
        type: 'Just',
        value: new RegExp(that[1], that[2])
      };
    } else if (options.explicit) {
      return {
        type: 'Nothing'
      };
    } else {
      return {
        type: 'Just',
        value: new RegExp(value)
      };
    }
  },
  Array: function(it){
    return coerceArray(it, {
      of: [{
        type: '*'
      }]
    });
  },
  Object: function(it){
    return coerceFields(it, {
      of: {}
    });
  },
  String: function(it){
    var that;
    if (that = it.match(/^'(.*)'$/)) {
      return {
        type: 'Just',
        value: that[1]
      };
    } else if (that = it.match(/^"(.*)"$/)) {
      return {
        type: 'Just',
        value: that[1]
      };
    } else {
      return {
        type: 'Just',
        value: it
      };
    }
  }
};
function coerceArray(node, type){
  var typeOf, element;
  if (toString$.call(node).slice(8, -1) !== 'Array') {
    return {
      type: 'Nothing'
    };
  }
  typeOf = type.of;
  return {
    type: 'Just',
    value: (function(){
      var i$, ref$, len$, results$ = [];
      for (i$ = 0, len$ = (ref$ = node).length; i$ < len$; ++i$) {
        element = ref$[i$];
        results$.push(coerceTypes(element, typeOf));
      }
      return results$;
    }())
  };
}
function coerceTuple(node, type){
  var i, types;
  if (toString$.call(node).slice(8, -1) !== 'Array') {
    return {
      type: 'Nothing'
    };
  }
  return {
    type: 'Just',
    value: (function(){
      var i$, ref$, len$, results$ = [];
      for (i$ = 0, len$ = (ref$ = type.of).length; i$ < len$; ++i$) {
        i = i$;
        types = ref$[i$];
        results$.push(coerceTypes(node[i], types));
      }
      return results$;
    }())
  };
}
function coerceFields(node, type){
  var typeOf, key, value;
  if (toString$.call(node).slice(8, -1) !== 'Object') {
    return {
      type: 'Nothing'
    };
  }
  typeOf = type.of;
  return {
    type: 'Just',
    value: (function(){
      var ref$, results$ = {};
      for (key in ref$ = node) {
        value = ref$[key];
        results$[key] = coerceTypes(value, typeOf[key] || [{
          type: '*'
        }]);
      }
      return results$;
    }())
  };
}
function coerceType(node, typeObj, options){
  var type, structure, coerceFunc;
  type = typeObj.type, structure = typeObj.structure;
  if (type) {
    coerceFunc = types[type];
    return coerceFunc(node, options);
  } else {
    switch (structure) {
    case 'array':
      return coerceArray(node, typeObj);
    case 'tuple':
      return coerceTuple(node, typeObj);
    case 'fields':
      return coerceFields(node, typeObj);
    }
  }
}
function coerceTypes(node, types, options){
  var i$, len$, type, ref$, valueType, value;
  options == null && (options = {});
  for (i$ = 0, len$ = types.length; i$ < len$; ++i$) {
    type = types[i$];
    ref$ = coerceType(node, type, options), valueType = ref$.type, value = ref$.value;
    if (valueType === 'Nothing') {
      continue;
    }
    if (parsedTypeCheck([type], value)) {
      return value;
    }
  }
  throw new Error("Value '" + node + "' does not type check against " + JSON.stringify(types) + ".");
}
function consumeOp(tokens, op){
  if (tokens[0] === op) {
    return tokens.shift();
  } else {
    throw new Error("Expected '" + op + "', but got " + tokens[0] + " instead.");
  }
}
function maybeConsumeOp(tokens, op){
  if (tokens[0] === op) {
    return tokens.shift();
  }
}
function consumeList(tokens, delimiters, hasDelimiters){
  var result;
  if (hasDelimiters) {
    consumeOp(tokens, delimiters[0]);
  }
  result = [];
  while (tokens.length && tokens[0] !== delimiters[1]) {
    result.push(consumeElement(tokens));
    maybeConsumeOp(tokens, ',');
  }
  if (hasDelimiters) {
    consumeOp(tokens, delimiters[1]);
  }
  return result;
}
function consumeArray(tokens, hasDelimiters){
  return consumeList(tokens, ['[', ']'], hasDelimiters);
}
function consumeTuple(tokens, hasDelimiters){
  return consumeList(tokens, ['(', ')'], hasDelimiters);
}
function consumeFields(tokens, hasDelimiters){
  var result, key;
  if (hasDelimiters) {
    consumeOp(tokens, '{');
  }
  result = {};
  while (tokens.length && (!hasDelimiters || tokens[0] !== '}')) {
    key = tokens.shift();
    consumeOp(tokens, ':');
    result[key] = consumeElement(tokens);
    maybeConsumeOp(tokens, ',');
  }
  if (hasDelimiters) {
    consumeOp(tokens, '}');
  }
  return result;
}
function consumeElement(tokens){
  switch (tokens[0]) {
  case '[':
    return consumeArray(tokens, true);
  case '(':
    return consumeTuple(tokens, true);
  case '{':
    return consumeFields(tokens, true);
  default:
    return tokens.shift();
  }
}
function consumeTopLevel(tokens, types){
  var structure, origTokens, result;
  structure = types[0].structure;
  if (types.length === 1 && structure) {
    origTokens = tokens.slice();
    result = structure === 'array'
      ? consumeArray(tokens, tokens[0] === '[')
      : structure === 'tuple'
        ? consumeTuple(tokens, tokens[0] === '(')
        : consumeFields(tokens, tokens[0] === '{');
    if (tokens.length) {
      return consumeElement(structure === 'array'
        ? ['['].concat(origTokens, [']'])
        : ['('].concat(origTokens, [')']));
    } else {
      return result;
    }
  } else {
    return consumeElement(tokens);
  }
}
tokenRegex = /("(?:[^"]|\\")*")|('(?:[^']|\\')*')|(#.*#)|(\/(?:\\\/|[^\/])*\/[gimy]*)|([\[\]\(\)}{:,])|([-\.\$\w]+)|\s*/;
function coerce(types, string){
  var tokens, node;
  tokens = reject(function(it){
    return !it || /^\s+$/.test(it);
  }, string.split(tokenRegex));
  node = consumeTopLevel(tokens, types);
  if (!node) {
    throw new Error("Error parsing " + string);
  }
  return coerceTypes(node, types);
}
module.exports = coerce;
/*
function log
  console.log it; it
*/

}).call(this);