Skip to content

Instantly share code, notes, and snippets.

@Heimdell
Last active April 1, 2018 14:22
Show Gist options
  • Save Heimdell/dce750a918b64218a0583ffc5bd66744 to your computer and use it in GitHub Desktop.
Save Heimdell/dce750a918b64218a0583ffc5bd66744 to your computer and use it in GitHub Desktop.
Partitioned CPS transfrom from there: http://matt.might.net/articles/cps-conversion/
// Object.prototype.ancestors = function () {
// let acc = []
// for (let i = this; i; i = Object.getPrototypeOf(i)) {
// acc.push(i)
// }
// return acc
// }
let automatch = function (proto) {
let matcher = eval(`
value => function (...args) {
return value(this, ...args)
}
`)
let acc = {}
for (let key of Object.keys(proto)) {
let value = proto[key]
if (value instanceof Function) {
acc[key] = function(...args) {
return value(this, ...args)
}
} else {
acc[key] = value
}
}
return acc
}
let modifiable = (proto) => ({
modify(schema) {
let acc = {}
for (let key of Object.keys(schema)) {
let modifier = schema[key]
let newValue = modifier(this[key])
acc[key] = newValue
}
let target = {}
target.__proto__ = proto
return Object.assign(target, this, acc)
}
})
let injectProto = (proto, factory) => {
let next = factory.prototype.__proto__
proto.__proto__ = next
factory.prototype.__proto__ = proto
}
let adt = (name, fieldstr, ...protos) => {
let fields = fieldstr.split(", ")
// BECAUSE I HATE new OPERATOR, and the classes are ugly and filled with garbage
let ctor = eval(`
function ${name}(${fields}) {
if (new.target) {
Object.assign(this, {${fields}})
Object.freeze(this)
} else {
return new ${name}(${fields})
}
}
${name}
`)
let layer = Object.assign({}, modifiable(ctor.prototype), ...protos.map(automatch))
injectProto(layer, ctor)
return ctor
}
module.exports = adt
let data = require('./adt')
let util = require('util')
let i = ({
toString: (it) => it.inspect()
})
let index = 0
let gensym = (k, w) => w(k + index++)
let Var = data("Var", "name", i, {
inspect: ({name}) => `$${name}`,
tk: (it, k) => k(it.m()),
tc: (it, c) => Goto(c, it.m()),
m : ({name}) => Term(name),
to: (it, body) => Lam(it, body),
apply: (it, arg) => App(it, arg),
})
let Lam = data("Lam", "x, body", i, {
inspect: ({x, body}) => `{${x} -> ${body}}`,
tk: (it, k) => k(it.m()),
tc: (it, c) => Goto(c, it.m()),
m : ({x, body}) => {
let k = gensym('k', Label)
return Fun(x, k, body.tc(k))
},
apply: (it, arg) => App(it, arg),
})
let App = data("App", "f, x", {
inspect: ({f, x}) => `(${f} ${x})`,
tk: ({f, x}, k) => {
let rv = gensym('rv', Term)
let cont = Cont(rv, k(rv))
return (
f.tk(f =>
x.tk(x =>
Call(f, x, cont)))
)
},
tc: ({f, x}, c) => f.tk(f => x.tk(x => Call(f, x, c))),
m : ({f, x}) => { throw Error("App.m: imposible") },
apply: (it, arg) => App(it, arg),
})
let flip = (f) => (x, y) => f(y, x)
let fun = (args, body) => args.reduceRight(flip(Lam), body)
let call = (f, ...xs) => xs.reduce(App, f)
let Term = data("Term", "name", i, {
inspect: ({name}) => `#${name}`,
})
let Label = data("Label", "name", i, {
inspect: ({name}) => `${name}?`,
})
let Fun = data("Closure", "x, ret, body", i, {
inspect: ({x, ret, body}) => `(${x} ${ret} => ${body}}`,
})
let Cont = data("Closure", "x, body", i, {
inspect: ({x, body}) => `{${x} => ${body}}`,
})
let Call = data("Call", "f, x, ret", i, {
inspect: ({f, x, ret}) => `(${f} ${x} ${ret})`,
})
let Goto = data("Goto", "ret, x", i, {
inspect: ({ret, x}) => `(${ret} ${x})`,
})
let Halt = data("Halt", "", i, {
inspect: ({}) => `Halt`,
})()
console.log(Var("a"))
let [x, xs, sel] = ["x", "xs", "sel"].map(Var)
console.log(fun([x, xs, sel], call(sel, x, xs)).tc(Halt))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment