// Generated by CoffeeScript 1.12.2
(function() {
  var slice = [].slice;

  module.exports = function(ruleSpecs0) {
    var action, isArray, matcher, matchersFromString, matchingPrefixLength, rule, ruleSpecs, rules, s, splitAt, typeTest;
    isArray = require("util").isArray;
    matchingPrefixLength = function(test, elms) {
      var elm, i, j, len1;
      for (i = j = 0, len1 = elms.length; j < len1; i = ++j) {
        elm = elms[i];
        if (!test(elm)) {
          return i;
        }
      }
      return elms.length;
    };
    splitAt = function(i, arr) {
      return [arr.slice(0, i), arr.slice(i)];
    };
    typeTest = function(type) {
      switch (type) {
        case 's':
          return function(x) {
            return typeof x === "string";
          };
        case 'n':
          return function(x) {
            return typeof x === "number";
          };
        case 'b':
          return function(x) {
            return typeof x === "boolean";
          };
        case 'o':
          return function(x) {
            return (typeof x === "object") && (x != null) && !isArray(x);
          };
        case 'f':
          return function(x) {
            return typeof x === "function";
          };
        case 'a':
          return function(x) {
            return isArray(x);
          };
        case '.':
          return function(x) {
            return typeof x !== "undefined";
          };
        default:
          throw new Error("Don't know how to test for type '" + type + "'");
      }
    };
    matcher = function(type, multiplicity) {
      var m, test;
      if (multiplicity == null) {
        multiplicity = '';
      }
      test = typeTest(type);
      m = (function() {
        switch (multiplicity) {
          case '':
            return function(arg1) {
              var arg, args;
              arg = arg1[0], args = 2 <= arg1.length ? slice.call(arg1, 1) : [];
              if (test(arg)) {
                return [arg, args];
              }
            };
          case '?':
            return function(arg1) {
              var arg, args;
              arg = arg1[0], args = 2 <= arg1.length ? slice.call(arg1, 1) : [];
              if (test(arg)) {
                return [arg, args];
              } else if (arg != null) {
                return [null, [arg].concat(slice.call(args))];
              } else {
                return [null, args];
              }
            };
          case '+':
            return function(args) {
              var len;
              len = matchingPrefixLength(test, args);
              if (len > 0) {
                return splitAt(len, args);
              }
            };
          case '*':
            return function(args) {
              var len;
              len = matchingPrefixLength(test, args);
              return splitAt(len, args);
            };
          default:
            throw new Error("What do you mean - '" + type + "','" + multiplicity + "'?");
        }
      })();
      m.label = type + multiplicity;
      return m;
    };
    matchersFromString = function(s) {
      var j, len1, multiplicity, ref, ref1, results, type;
      if (s.trim().length === 0) {
        return [];
      }
      ref = s.split(',');
      results = [];
      for (j = 0, len1 = ref.length; j < len1; j++) {
        ref1 = ref[j], type = ref1[0], multiplicity = ref1[1];
        results.push(matcher(type, multiplicity));
      }
      return results;
    };
    rule = function(matchers, action) {
      return function(args) {
        var consumed, j, len1, match, rest, result;
        rest = args;
        result = [];
        for (j = 0, len1 = matchers.length; j < len1; j++) {
          matcher = matchers[j];
          match = matcher(rest);
          if (match == null) {
            return false;
          } else {
            consumed = match[0], rest = match[1];
            result.push(consumed);
          }
        }
        if (rest.length > 0) {
          return false;
        }
        return function() {
          return action.apply(this, result);
        };
      };
    };
    if (typeof ruleSpecs0 === "function") {
      ruleSpecs = [];
      ruleSpecs0(function(str, action) {
        return ruleSpecs.push([str, action]);
      });
    } else {
      ruleSpecs = ruleSpecs0;
    }
    rules = (function() {
      var j, len1, ref, results;
      results = [];
      for (j = 0, len1 = ruleSpecs.length; j < len1; j++) {
        ref = ruleSpecs[j], s = ref[0], action = ref[1];
        results.push(rule(matchersFromString(s), action));
      }
      return results;
    })();
    return function() {
      var args, j, len1;
      args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
      for (j = 0, len1 = rules.length; j < len1; j++) {
        rule = rules[j];
        action = rule(args);
        if (action) {
          return action.apply(this);
        }
      }
    };
  };

}).call(this);
