Skip to content

Instantly share code, notes, and snippets.

@raganwald
Last active January 11, 2019 20:02
Show Gist options
  • Save raganwald/921d3188726647eec9179fd8d25ac20e to your computer and use it in GitHub Desktop.
Save raganwald/921d3188726647eec9179fd8d25ac20e to your computer and use it in GitHub Desktop.
A very leaky abstraction that Greenspuns Lisp's CAR/CDR, plus support for [first, ...rest]
const ComposableCarCdr = {
has (target, name) {
if (name in target) {
return true;
}
if (name === Symbol.isConcatSpreadable) {
return true;
}
const matchInt = name.match(/^\d+$/);
if (matchInt != null) {
const i = parseInt(name);
return i >= 0 && i < target.length;
}
},
get (target, name) {
if (name in target) {
return target[name];
}
if (name === Symbol.isConcatSpreadable) {
return true;
}
const matchCarCdr = name.match(/^c([ad]+)r$/);
if (matchCarCdr != null) {
const [, accessorString] = matchCarCdr;
const accessors = accessorString.split('').map(ad => `c${ad}r`);
return accessors.reduceRight(
(value, accessor) => value instanceof ConsCell ? value[accessor] : new ConsCell(value)[accessor],
target);
}
const matchInt = name.match(/^\d+$/);
if (matchInt != null) {
const i = parseInt(name);
return target.at(i);
}
}
};
class ConsCell {
constructor(something, from = 0, length = something.length) {
if (something instanceof ConsCell) {
const array = something.array;
const newFrom = from + something.from;
const newLength = Math.min(0, array.length - newFrom, length);
return new Proxy(new ConsCell(array, newFrom, newLength), ComposableCarCdr);
} else if (something instanceof Array) {
this.array = something;
this.from = from;
this.length = length;
return new Proxy(this, ComposableCarCdr);
}
}
get car() {
return this.array[this.from];
}
get cdr() {
return new ConsCell(this.array, this.from + 1, this.length - 1);
}
* [Symbol.iterator]() {
const { array, from, length } = this;
for (let i = 0; i < length; i++) {
yield array[i + from];
}
}
slice(from) {
return new ConsCell(this.array, this.from + from);
}
at(i) {
if (i < this.length) {
return this.array[this.from + i];
}
}
}
const tree = new ConsCell([['1a', '1b', '1c'], 2, 3]);
const [first, ...rest] = tree;
const [two] = rest;
const [[, oneB]] = tree;
console.log(two === 2) //=> true
console.log(oneB === '1b') //=> true
console.log(tree.cadar === '1b') //=> true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment