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.
151 lines
2.7 KiB
151 lines
2.7 KiB
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); |
|
} |
|
}
|
|
|