Pattern matching for Javascript data structures (objects / lists / primitives) in a DSLy fashion

by ntoshev

HTML

<script src="https://github.com/documentcloud/underscore/raw/master/underscore-min.js"></script>
<div id='log'/>

JavaScript

//make console.log print to the html panel
var logs=document.getElementById('log');
console.log= function(s){
var e=document.createElement('pre');
    e.innerHTML=s;
    logs.insertBefore(e);
}

'use strict';

//create variables to be matched
function v(name){
    function v(value) {
        if (v.bound != undefined && value != undefined && v.bound != value)
            throw new ReferenceError('Conflict in ' + name + ' between ' + v.bound + ' and ' + value)
        if (value != undefined)
            v.bound = value
        return v.bound
    }
    return v
}

// determine if v is a matchable variable
function isVar(v){
    var props = []
    for (var p in x){
        props.push(p)
    }
    //better typecheck? Make it a real object, instanceof?
    return (v instanceof Function) && (_.isEqual(props,[]) || _.isEqual(props,['bound']))
}

function match(first, second) {
    if (isVar(first)) {
        first(second)
    } else if (isVar(second)) {
        second(first)
    } else if (first instanceof Array) {
        if (!(second instanceof Array)) throw ReferenceError("Can't match " + first + " with " + second)
        if (first.length != second.length) throw ReferenceError("Array length doesn't match")
        for (var i=0; i<second.length; i++) 
            match(first[i], second[i])
    } else if (first instanceof Object) {
        for(var p in first) {
            if (!(p in second)) throw ReferenceError("Can't match property " + p)
            match(first[p], second[p])
        }
        for(var p in second) {
            if (!(p in first)) throw ReferenceError("Can't match property " + p)
        }
    } else if (first != second) {
        throw ReferenceError("Can't match " + first + " with " + second)
    }
}

var x = v();
var y = v()
match({name:'john', list:[y,'john', 3, 'john']}, {name:x, list:[1, x, 3, x]});
console.log(x()); // john
console.log(y()); // 1
match(y, 2); // exception printed in console, y is 1 already
console.log(y()); // never reached