natefaubion / matches.js

Powerful pattern matching for Javascript
MIT License
772 stars 37 forks source link

Allow arbitrary patterns to be combined with rest expressions #7

Closed natefaubion closed 12 years ago

natefaubion commented 12 years ago

As per the discussion in issue #5 with @slavah

The idea being that rest expressions shouldn't be limited to just identifier, but could be combined with any other pattern, with that pattern being applied over each element.

For example:

pattern({
  '[...{myKeyA: c, myKeyB: d}]': function (c, d) {
    // The above object pattern has been applied to every 
    // element in the array, extracting the values out and putting 
    // them into their own arrays.
    // c contains only myKeyA values
    // d contains only myKeyB values
  }
});

Here is an example implementation of how that might compile:

function matchFn (args, runt) {
  var ret = [];
  if (!(args instanceof Array) || args.length !== 1) return false;
  var args_0 = args[0];
  if (!(args_0 instanceof Array) || args.length < 0) return false;
  var args_0_pos = 0; 
  // The number of identifiers in the rest expression need to be 
  // counted so we can initialize the return buffers.
  var args_0_ret = [[], []];
  // Create a function that we will call on each iteration. We 
  // will shadow `ret`so we can reuse all the same compiler 
  // functions.
  var args_0_loop = function (val) {
    var ret = [];
    if (!(val instanceof Object)) return false;
    var val_keys = {myKeyA: 1, myKeyB: 1};
    var val_count = 0;
    for (var val_i in val) {
      if (!val.hasOwnProperty(val_id)) return false;
      else val_count += 1;
    }
    if (val_count !== 1) return false;
    var val_0 = val["myKeyA"];
    ret.push(val_0);
    var val_1 = val["myKeyB"]
    ret.push(val_1);
    return ret
  };
  // Loop over the array
  var args_0_i = 0, args_0_len = args_0.length, args_0_retargs;
  for (; args_0_i < args_0_len) {
    args_0_retargs = args_0_loop(args_0[args_0_i]);
    if (args_0_retargs === false) return false;
    // Push each items in retargs to the matching buffer.
    args_0_ret[0].push(args_0_retargs[0]);
    args_0_ret[1].push(args_0_retargs[1]);
  }
  // Concat the ret array with the array of buffers.
  ret = Array.prototype.concat.apply(ret, args_0_ret);
  return ret;
}

The main takeaway being we create a sub function that acts as its own isolated match function that we apply to each element in the array. The return values are individually pushed onto different buffers, with those buffers finally being concated with the functions ret array. It's a little hairy, but it lets us reuse all of the same compiler functions.

Comments and improvements are welcome.

natefaubion commented 12 years ago

Added in upcoming 0.5 release