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); } }