var defineType = function(name,subtypes){
var subtype,
_super = function(){};
Object.defineProperty(Object.prototype,name+"_q",{
get:function(){return this instanceof _super;}
});
for(subtype in subtypes){
(function(fields){
var len = fields.length,
construct = function(){
var that = (this instanceof construct)?this:
Object.create(construct.prototype);
for(var i = 0;i<len;i++){
that[fields[i]] = arguments[i];
}
return that;
};
construct.prototype = new _super();
construct.prototype.constructor = construct;
this[subtype] = construct;
Object.defineProperty(Object.prototype,subtype+"_q",{
get:function(){return this instanceof construct;}
});
}(subtypes[subtype]));
}
};
defineType('WAE',{
num:['n'],
binop:['op','lhs','rhs'],
def:['lob','body'],
id:['name']
});
This produces globally accessible constructors for num, binop, def,
and id, and boolean object properties .num_q, .binop_q, .def_q, .id_q,
and .WAE_q.
(5).num_q === false
id('x').num_q === false;
num(5).num_q === true
num(5).WAE_q === true
Ah, I thought of that, and fixed it.
In a browser environment, the assignment fails silently, and the type-
querying properties maintain their true values.
In node, you get this:
var x = num(5);
try{ x.num_q = false;
x.id_q = true;
}catch(e){console.log(e.message);}
> Error: Cannot set property num_q of #<Object>
-logan.
function defineType(name,subtypes){
var subtype,
_super = function(){};
this[name] = _super;
Object.defineProperty(Object.prototype,name+"_q",{
get:function(){return this instanceof _super;}
});
for(subtype in subtypes){
(function(fields,type){
var len = fields.length,
construct = function(){
var that = (this instanceof construct)?this:
Object.create(construct.prototype);
for(var i = 0;i<len;i++){
that[fields[i]] = arguments[i];
}
that._type = type;
return that;
};
construct.prototype = new _super();
construct.prototype.constructor = construct;
_super[type] = fields;
this[type] = construct;
Object.defineProperty(Object.prototype,subtype+"_q",{
get:function(){return this instanceof construct;}
});
}(subtypes[subtype],subtype));
}
}
function typeCase(type,arg,cases){
var subtype;
if(!(arg instanceof type)) throw "Type mismatch";
if(typeof cases._ === 'undefined'){
for(subtype in type) if(type.hasOwnProperty(subtype)){
if(typeof cases[subtype]==='undefined') throw "Missing "+subtype+"
case";
}
}
if(arg._type in cases){
return
cases[arg._type].apply(this,type[arg._type].map(function(fname){return
arg[fname];}));
}else{
return cases._();
}
}
defineType('CFWAE',{
num:['n'],
binop:['op','lhs','rhs'],
def:['lob','body'],
id:['name'],
if0:['c','then','else'],
fun:['args','body'],
app:['f','args']});
console.log(
typeCase(CFWAE,num(5),{
num:function(n){return "num "+n;},
binop:function(op,lhs,rhs){return "binop "+op+lhs+rhs;},
def:function(bindings,body){return "def "+bindings+body;},
_:function(){return "Else";}
})
);
typeCase is really inefficient, but it does work. You can get the same
functionality with better performance by just doing
switch(arg.constructor){
case num: ....
}
which is how I've implemented the translation of xinterp into
JavaScript.
-l.