You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
152 lines
2.7 KiB
152 lines
2.7 KiB
3 years ago
|
export const DataTypes = {
|
||
|
Symbol: 'symbol',
|
||
|
Nil: 'nil',
|
||
|
True: 'true',
|
||
|
Number: 'number',
|
||
|
String: 'string',
|
||
|
Function: 'function',
|
||
|
SpecialForm: 'special-form',
|
||
|
Cons: 'cons',
|
||
|
Macro: 'macro'
|
||
|
}
|
||
|
|
||
|
export class DataType {
|
||
|
constructor (type) {
|
||
|
this.type = type;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export class Atom extends DataType {
|
||
|
constructor (type, value) {
|
||
|
super(type);
|
||
|
this.value = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export class Symbol extends Atom {
|
||
|
constructor (value) {
|
||
|
super(DataTypes.Symbol, value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export class Nil extends Atom {
|
||
|
constructor() {
|
||
|
super(DataTypes.Nil, null);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export class True extends Atom {
|
||
|
constructor() {
|
||
|
super(DataTypes.True, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export class Number extends Atom {
|
||
|
constructor(value) {
|
||
|
super(DataTypes.Number, value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export class String extends Atom {
|
||
|
constructor(value) {
|
||
|
super(DataTypes.String, value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export class Function extends Atom {
|
||
|
constructor(value, sexp, env) {
|
||
|
super(DataTypes.Function, value);
|
||
|
this.sexp = sexp;
|
||
|
this.env = env;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export class SpecialForm extends Atom {
|
||
|
constructor(value) {
|
||
|
super(DataTypes.SpecialForm, value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export class Cons extends DataType {
|
||
|
constructor(car = new Nil(), cdr = new Nil()) {
|
||
|
super(DataTypes.Cons);
|
||
|
this.car = car;
|
||
|
this.cdr = cdr;
|
||
|
}
|
||
|
|
||
|
// Optimise for tail call.
|
||
|
static async reduce(cons, fn, start) {
|
||
|
if (cons.car) {
|
||
|
start = await fn(start, cons.car);
|
||
|
}
|
||
|
|
||
|
if (cons.cdr.type == DataTypes.Nil) {
|
||
|
return start;
|
||
|
}
|
||
|
|
||
|
if (cons.cdr.type === DataTypes.Cons) {
|
||
|
return await Cons.reduce(cons.cdr, fn, start);
|
||
|
} else {
|
||
|
return await fn(start, cons.cdr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Optimise for tail call.
|
||
|
static async map(cons, fn) {
|
||
|
if (cons.car) {
|
||
|
return new Cons(
|
||
|
await fn(cons.car),
|
||
|
cons.cdr.type === DataTypes.Nil
|
||
|
? cons.cdr
|
||
|
: await Cons.map(cons.cdr, fn)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return cons;
|
||
|
}
|
||
|
|
||
|
static zip(cons, list) {
|
||
|
const fn = (cons, list, result) => {
|
||
|
result = new Cons(new Cons(cons.car, list.car), result);
|
||
|
if (cons.cdr.type === DataTypes.Nil || list.cdr === DataTypes.Nil)
|
||
|
{
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
return fn(cons.cdr, list.cdr, result);
|
||
|
}
|
||
|
|
||
|
return Cons.reverse(fn(cons, list, new Nil));
|
||
|
}
|
||
|
|
||
|
static reverse(cons) {
|
||
|
const fn = (cons, last) => {
|
||
|
let next = cons.cdr;
|
||
|
cons.cdr = last;
|
||
|
if (next.type === DataTypes.Nil) {
|
||
|
return cons;
|
||
|
}
|
||
|
return fn(next, cons);
|
||
|
};
|
||
|
return fn(cons, new Nil());
|
||
|
}
|
||
|
|
||
|
static nth(cons, i) {
|
||
|
if (i > 0) {
|
||
|
return Cons.nth(cons.cdr.nth, i - 1);
|
||
|
}
|
||
|
|
||
|
return cons.car;
|
||
|
}
|
||
|
|
||
|
static last(cons) {
|
||
|
return Cons.reverse(cons).car;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export class Macro extends Atom {
|
||
|
constructor(value) {
|
||
|
super(DataTypes.Macro, value);
|
||
|
}
|
||
|
}
|