Multiple implementations (JS, Wasm, C) of a Lisp.
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

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