/*
 *  Copyright (C) 2008 Adrien Friggeri
 *  
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program; see the file COPYING. If not, write to the
 *  Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
var Pattern_Matching;

(function () {
  function typeOf(value) {
    var s = typeof value;
    if (s === 'object') {
        if (value) {
            if (value instanceof Array) {
                s = 'array';
            }
        } else {
            s = 'null';
        }
    }
    return s;
  }

  Pattern_Matching = function () {
    var that = this;
    var id = Math.random ();
    var idname = Math.ceil (10000 * Math.random ());

    this.variable = function (name) {
      var o = {};
      o.name = name;
      o['__match_pattern_id'+idname] = id;
      return o;
    }

    function is_variable (s) {
      return (s && s['__match_pattern_id'+idname] && s['__match_pattern_id'+idname] == id);
    }

    function unify (subject, pattern) {
      if (is_variable(pattern)) {
        return {matched:true, acc:[{name:pattern.name, value:subject}]}
      } else {
        if (subject === pattern) {
          return {matched:true, acc:[]};
        } else {
          switch(typeof(pattern)) {
            case "array":
            case "object":
              var acc = [];
              for (var p in pattern) {
                if (typeOf(pattern) == "object" && typeOf(subject[p]) == "undefined") return {matched:false};
                var u = unify(subject[p], pattern[p]);
                if (u.matched) {
                  acc = acc.concat(u.acc);
                } else {
                  return {matched:false}
                }
              };
              return {matched:true, acc:acc};
            default:
              return {matched:false}
          }
        }
      }
    }

    function make_obj (acc) {
      var o = {};
      for (p in acc) {
        o[acc[p].name] = acc[p].value;
      };
      return o;
    }

    this.match = function (subject, patterns) {
      for (p in patterns) {
        var pattern = patterns[p]
        var u = unify(subject, pattern[0]);
        if (u.matched) {
          return (pattern[1]) (make_obj(u.acc));
        } 
      }
      return null;
    }
  };
}) ();

