Last active
January 11, 2019 20:02
-
-
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]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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