diff --git a/src/bf.ts b/src/bf.ts index 6f303b3..9114a3e 100644 --- a/src/bf.ts +++ b/src/bf.ts @@ -1,4 +1,4 @@ -import { _, cnst, g, id, l, m, pipe, r } from './core'; +import { _, cnst, g, id, l, n, pipe, r } from './core'; import { iif } from './bool'; import { Zero, ge, ifz, pred, succ } from './num'; import { x00 } from './byte'; @@ -7,7 +7,7 @@ import { Pair, first, fst, second, snd, uncurry } from './pair'; import { cons, head, repeat, set, singleton, tail, update } from './list'; import { Empty, get, len, snoc } from './string'; -m('bf'); +n('bf'); const done = g('done', uncurry._(l('p', cnst._(uncurry._(l('is', l('cs', ge._(head._('is'))._(len._('cs')))))._('p'))))); @@ -36,15 +36,15 @@ const next = g('next', second._(first._(first._(succ)))); const prev = g('prev', second._(first._(first._(pred)))); const jump = g('jump', l('d', l('s', - iif._(command._(_c('['))._('s'))._(r('bf::jump')._(succ._('d'))) - ._(iif._(command._(_c(']'))._('s'))._(ifz._(pred._('d'))._(cnst._('s'))._(r('bf::jump')._(pred._('d')))) - ._(r('bf::jump')._('d')))._(advance._('s'))))); + iif._(command._(_c('['))._('s'))._(r('bf', 'jump')._(succ._('d'))) + ._(iif._(command._(_c(']'))._('s'))._(ifz._(pred._('d'))._(cnst._('s'))._(r('bf', 'jump')._(pred._('d')))) + ._(r('bf', 'jump')._('d')))._(advance._('s'))))); const whlnz = g('whlnz', l('s', ifn._(read._('s'))._(jump._(Zero))._(save)._('s'))); const endwhl = g('endwhl', l('s', ifn._(read._('s'))._(reset)._(pipe._(save)._(restore))._('s'))); -const exec = g('exec', l('s', iif._(done._('s'))._('s')._(r('bf::exec')._(advance +const exec = g('exec', l('s', iif._(done._('s'))._('s')._(r('bf', 'exec')._(advance ._(iif._(command._(_c('>'))._('s'))._(next) ._(iif._(command._(_c('<'))._('s'))._(prev) ._(iif._(command._(_c('+'))._('s'))._(inc) diff --git a/src/bool.show.ts b/src/bool.show.ts index 92f0589..561d21a 100644 --- a/src/bool.show.ts +++ b/src/bool.show.ts @@ -1,7 +1,7 @@ -import { m, g, l } from './core'; +import { n, g, l } from './core'; import { iif } from './bool'; import { fromString as _s } from './string'; -m('bool.show'); +n('bool.show'); export const show = g('show', l('b', iif._('b')._(_s('True'))._(_s('False')))); diff --git a/src/bool.ts b/src/bool.ts index 07dc1e5..245010d 100644 --- a/src/bool.ts +++ b/src/bool.ts @@ -1,7 +1,7 @@ -import { $, Term, _, c, g, id, l, m, pipe } from './core'; +import { $, Term, _, g, id, l, n, pipe, r } from './core'; import { EQ, GT, LT } from './ord'; -m('bool'); +n('bool'); export const True = g('True', l('t', l('_', 't'))); @@ -9,15 +9,15 @@ export const False = g('False', l('_', l('f', 'f'))); export const iif = g('iif', id); -export const not = g('not', l('b', c('b', False, True))); +export const not = g('not', l('b', r('b')._(False)._(True))); -export const cmp = g('cmp', l('l', l('r', c('l', c('r', EQ, GT), c('r', LT, EQ))))); +export const cmp = g('cmp', l('l', l('r', r('l')._(r('r')._(EQ)._(GT))._(r('r')._(LT)._(EQ))))); -export const lt = g('lt', l('l', c('l', False))); +export const lt = g('lt', l('l', r('l')._(False))); export const le = g('le', l('l', not._('l')._(True))); -export const eq = g('eq', l('l', l('r', c('l', 'r', not._('r'))))); +export const eq = g('eq', l('l', l('r', r('l')._('r')._(not._('r'))))); export const ge = g('ge', l('l', pipe._(not)._(lt._('l')))); @@ -25,9 +25,9 @@ export const gt = g('gt', l('l', pipe._(not)._(le._('l')))); export const and = g('and', l('l', not._('l')._(False))); -export const or = g('or', l('l', c('l', True))); +export const or = g('or', l('l', r('l')._(True))); -export const xor = g('xor', l('l', l('r', c('l', not._('r'), 'r')))); +export const xor = g('xor', l('l', l('r', r('l')._(not._('r'))._('r')))); export const fromBoolean: (b: boolean) => Term = b => b ? True : False; diff --git a/src/byte.show.ts b/src/byte.show.ts index 4c41d45..5ec38e0 100644 --- a/src/byte.show.ts +++ b/src/byte.show.ts @@ -1,5 +1,5 @@ -import { m } from './core'; +import { n } from './core'; -m('byte.show'); +n('byte.show'); export { show } from './num.show'; diff --git a/src/byte.ts b/src/byte.ts index 917535b..0d73e6e 100644 --- a/src/byte.ts +++ b/src/byte.ts @@ -1,8 +1,8 @@ -import { Term, _, g, id, l, m, r } from './core'; +import { Term, _, g, id, l, n, r } from './core'; import { iif } from './bool'; import { Succ, Zero, fromNumber as _n, eq } from './num'; -m('byte'); +n('byte'); export const x00 = g('x00', Zero); @@ -16,10 +16,10 @@ export const inc = g('inc', Inc); export const dec = g('dec', l('n', r('n')._(xFF)._(id))); -export const sum = g('sum', l('l', l('r', r('r')._('l')._(r('byte::sum')._(inc._('l')))))); +export const sum = g('sum', l('l', l('r', r('r')._('l')._(r('byte', 'sum')._(inc._('l')))))); -export const sub = g('sub', l('l', l('r', r('r')._('l')._(r('byte::sub')._(dec._('l')))))); +export const sub = g('sub', l('l', l('r', r('r')._('l')._(r('byte', 'sub')._(dec._('l')))))); -export const mul = g('mul', l('l', l('r', r('l')._(x00)._(l('pl', sum._('r')._(r('byte::mul')._('pl')._('r'))))))); +export const mul = g('mul', l('l', l('r', r('l')._(x00)._(l('pl', sum._('r')._(r('byte', 'mul')._('pl')._('r'))))))); export const fromNumber: (n: number) => Term = n => _n(n % 256); diff --git a/src/char.show.ts b/src/char.show.ts index 65abc98..392baea 100644 --- a/src/char.show.ts +++ b/src/char.show.ts @@ -1,7 +1,7 @@ -import { g, l, m, pipe } from './core'; +import { g, l, n, pipe } from './core'; import { singleton } from './list'; import { wrap, fromString as _s } from './string'; -m('char.show'); +n('char.show'); export const show = g('show', l('c', pipe._(wrap._(_s('\'')))._(singleton)._('c'))); diff --git a/src/char.ts b/src/char.ts index 45fd104..405f264 100644 --- a/src/char.ts +++ b/src/char.ts @@ -1,8 +1,8 @@ -import { Term, g, l, m, pipe } from './core'; +import { Term, g, l, n, pipe } from './core'; import { toNumber as $n, fromNumber as _n, x00 } from './byte'; import { singleton } from './list'; -m('char'); +n('char'); export const Null = g('Null', x00); diff --git a/src/core.ts b/src/core.ts index a120f35..3e19ddf 100644 --- a/src/core.ts +++ b/src/core.ts @@ -1,4 +1,4 @@ -import { withLog } from './debug'; +import { withLog, withProfile } from './debug'; let index = 0; @@ -21,25 +21,47 @@ export abstract class Term { } public context(): string { - return !Object.keys(this.bindings).length ? `<${this.id}>` : `<${this.id}>[bound ${Object.keys(this.bindings).join(', ')}] `; + return `#${this.id}${this.cache ? '' : ''}${!Object.keys(this.bindings).length ? '' : ` [${Object.keys(this.bindings).join(', ')}]`}`; } - public stringify(context?: boolean): string { - return `${context ? this.context() : ''}${this.constructor.name}`; + public expression(): string { + return this.constructor.name; } - public _(arg: RefTerm): Term { - return c(this, arg); + public stringify(): string { + return `${this.context()} ${this.expression()}`; } + + public _(arg: Term): Term; + public _(name: string): Term; + public _(namespace: string, name: string): Term; + public _(argOrNamespace: Term | string, maybeName?: string): Term { + return new Call(this, termify(argOrNamespace as string, maybeName as string)); + } + + public toString(): string { + return this.expression(); + } + + public toJSON(): string { + return `<${this.expression()}>`; + } +} + +type Termify = { + (term: Term): Term; + (name: string): Term; + (namespace: string, name: string): Term; } +const termify: Termify = (termOrNameOrNamespace: Term | string, maybeName?: string) => + typeof termOrNameOrNamespace === 'string' ? r(termOrNameOrNamespace as string, maybeName as string) : termOrNameOrNamespace; + export class Undef extends Term { - constructor( - public from: string, - ) { super({}); } + constructor() { super({}); } override reduce(): Term { - throw new Error(`${this.from} = undef`); + throw new Error(`undef`); } } @@ -48,8 +70,12 @@ export class Native extends Term { public value: any, ) { super({}); } + public override expression(): string { + return `\`${JSON.stringify(this.value)}\`` + } + public override stringify(): string { - return `\`${JSON.stringify(this.value)}\``; + return this.expression(); } } @@ -70,27 +96,25 @@ export class Deferred extends Term { } public override reduce(): Term { - const term = this.cache - ? withLog( - `Executing ${this.stringify()}`, - () => this.cache!, - r => `got ${r.stringify()} (cached)`, - ) - : this.cache = withLog( - `Executing ${this.stringify()}`, - () => this.func({ ...globals, ...this.bindings }), - r => `got ${r.stringify()}`, - ); - return Object.entries(this.bindings).reduce((t, b) => t.bind(...b), term).reduce(); - } - - public override stringify(context: boolean = true): string { - return `${context ? this.context() : ''}\`deferred\``; + return this.cache = withLog( + `Executing ${this.stringify()}`, + () => withProfile( + `${this.expression()}${this.cache ? ' [cached]' : ''}`, + { cat: 'deferred' }, + () => this.cache ?? Object.entries(this.bindings).reduce((t, b) => t.bind(...b), this.func(this.bindings)).reduce(), + ), + r => `got ${r.stringify()}`, + ); + } + + public override expression(): string { + return '`deferred`'; } } export class Ref extends Term { constructor( + public namespace: string, public name: string, bindings: Record = {}, ) { super(bindings); } @@ -102,27 +126,23 @@ export class Ref extends Term { public override bind(name: string, term: Term): Term { return this.bindings[name] || !this.refs(name) ? this - : new Ref(this.name, { [name]: term }); + : new Ref(this.namespace, this.name, { [name]: term }); } public override reduce(): Term { - if (this.cache) { - return withLog( - `Substituting ${this.stringify()}`, - () => this.cache!, - r => `with ${r.stringify()} (cached)`, - ); - } else { - return this.cache = withLog( - `Substituting ${this.stringify()}`, - () => (this.bindings[this.name] ?? globals[this.name] ?? new Undef(this.name)).reduce(), - r => `with ${r.stringify()}`, - ).reduce(); - } - } - - public override stringify(context = true): string { - return `${context ? this.context() : ''}${this.name}`; + return this.cache = withLog( + `Substituting ${this.stringify()}`, + () => withProfile( + `${this.expression()}${this.cache ? ' [cached]' : ''}`, + { cat: 'ref' }, + () => this.cache ?? (this.namespace === 'local' ? this.bindings[this.name] : globals[`${this.namespace}::${this.name}`] ?? new Undef()).reduce(), + ), + r => `with ${r.stringify()}`, + ); + } + + override expression(): string { + return this.name; } } @@ -143,10 +163,10 @@ export class Lambda extends Term { : new Lambda(this.param, this.body.bind(name, term), { ...this.bindings, [name]: term }); } - public override stringify(context = true): string { + public override expression(): string { return this.body instanceof Lambda - ? `${context ? this.context() : ''}\\ ${this.param} ${this.body.stringify(false).slice(2)}` - : `${context ? this.context() : ''}\\ ${this.param} . ${this.body.stringify(false)}`; + ? `\\ ${this.param} ${this.body.expression().slice(2)}` + : `\\ ${this.param} . ${this.body.expression()}`; } } @@ -168,69 +188,79 @@ export class Call extends Term { } override reduce(): Term { - if (this.cache) { - return withLog( - `Reducing ${this.stringify()}`, - () => this.cache!, - r => `to ${r.stringify()} (cached)`, - ); - } else { - return this.cache = withLog( - `Reducing ${this.stringify()}`, - () => { + return this.cache = withLog( + `Reducing ${this.stringify()}`, + () => withProfile( + `${this.expression()}${this.cache ? ' [cached]' : ''}`, + { cat: 'call' }, + () => this.cache ?? (() => { const func = this.func.reduce(); - if (func instanceof Lambda) { - return withLog( - `Applying ${func.stringify()} to ${this.arg.stringify()}`, - () => func.body.bind(func.param, this.arg), - r => `got ${r.stringify()}`, - ).reduce(); + if (!(func instanceof Lambda)) { + return new Undef(); } - return this; - }, - r => `to ${r.stringify()}`, - ); - } + return withLog( + `Binding ${this.arg.stringify()} to ${func.param}`, + () => withProfile( + `${func.param} = ${this.arg.expression()}`, + { cat: 'binding' }, + () => func.body.bind(func.param, this.arg), + ), + r => `got ${r.stringify()}`, + ).reduce(); + })(), + ), + r => `to ${r.stringify()}`, + ); } - public override stringify(context = true): string { - return `${context ? this.context() : ''}${this.func instanceof Lambda ? `(${this.func.stringify(false)})` : this.func.stringify(false)} ${this.arg instanceof Ref || this.arg instanceof Native ? this.arg.stringify(false) : `(${this.arg.stringify(false)})`}`; + override expression(): string { + return `${this.func instanceof Lambda ? `(${this.func.expression()})` : this.func.expression()} ${this.arg instanceof Lambda || this.arg instanceof Call ? `(${this.arg.expression()})` : this.arg.expression()}`; } } const globals: Record = {}; -type RefTerm = string | Term; - -const termify = (t: RefTerm) => typeof t === 'string' ? r(t) : t; +let namespace: string = 'core'; -export const _ = (v: any) => new Native(v); -export const r = (name: string): Ref => new Ref(name); -export const l = (param: string, body: RefTerm): Lambda => new Lambda(param, termify(body)); -export const c = (func: RefTerm, arg: RefTerm, ...args: RefTerm[]): Call => - args.reduce((f: Call, a: RefTerm) => new Call(f, termify(a)), new Call(termify(func), termify(arg))); -export const d = (func: (bindings: Record) => Term) => new Deferred(func); +export const n = (name: string) => namespace = name; -export const $ = (t: Term): T => { - const rt = t.reduce(); - return rt instanceof Native ? rt.value : rt.stringify(); +export const g = (name: string, term: Term) => { + const full = `${namespace}::${name}`; + if (globals[full]) { + throw new Error(`Duplicate global: "${full}"`); + } + globals[full] = term; + return new Ref(namespace, name); }; -let cm: string = 'core'; +type Refify = { + (name: string): Ref; + (namespace: string, name: string): Ref; +} -export const m = (name: string) => cm = name; +export const r: Refify = (namespaceOrName: string, name?: string) => new Ref(name ? namespaceOrName : 'local', name ?? namespaceOrName); -export const g = (name: string, term: RefTerm) => { - name = `${cm}::${name}`; - if (globals[name]) { - throw new Error(`Duplicate global: "${name}"`); - } - globals[name] = termify(term); - return new Ref(name); +type Lambdify = { + (param: string, body: Term): Lambda; + (param: string, ref: string): Lambda; +} + +export const l: Lambdify = (param: string, body: Term | string): Lambda => new Lambda(param, termify(body as string)); + + +type Deferedify = { + (value: (bindings: Record) => Term): Deferred; + (value: boolean | number | string | object): Native; +} + +export const _: Deferedify = ((value: any) => (typeof value === 'function' ? new Deferred(value) : new Native(value))) as Deferedify; +export const $ = (t: Term): T => { + const rt = t.reduce(); + return rt instanceof Native ? rt.value : rt.stringify(); }; -export const undef = g('undef', new Undef('undef')); +export const undef = g('undef', new Undef()); export const id = g('id', l('f', 'f')); export const cnst = g('cnst', l('x', l('_', 'x'))); -export const pipe = g('pipe', l('f', l('g', l('x', c('f', c('g', 'x')))))); +export const pipe = g('pipe', l('f', l('g', l('x', r('f')._(r('g')._('x')))))); diff --git a/src/debug.ts b/src/debug.ts index 7e77a5c..e4d85f9 100644 --- a/src/debug.ts +++ b/src/debug.ts @@ -1,3 +1,5 @@ +import { writeFileSync } from 'node:fs'; + let indent: string = ''; const logs: string[] = []; @@ -6,6 +8,7 @@ export function log(): void { logging = true; } + export function withLog(before: string, f: () => T, after: (v: T) => string): T { if (!logging) { return f(); @@ -24,7 +27,48 @@ export function withLog(before: string, f: () => T, after: (v: T) => string): return result; } + +let profiling: boolean = false; +export function profile(): void { + profiling = true; +} + +let index = 0; +export function withProfile(name: string, detail: Record<'cat', string>, f: () => T) { + if (!logging) { + return f(); + } + + const i = index++; + performance.mark(`${name}::${i}::start`, { detail }); + const result = f(); + performance.mark(`${name}::${i}::end`, { detail }); + performance.measure(name, { + start: `${name}::${i}::start`, + end: `${name}::${i}::end`, + detail, + }); + return result; +} + export function dump() { - console.log('\n\nExecution log:'); - console.log(logs.join('\n')); + if (logging) { + console.log('\n\nExecution log:'); + console.log(logs.join('\n')); + } + if (profiling) { + writeFileSync('profile.json', JSON.stringify({ + pid: 0, + tid: 0, + displayTimeUnit: 'ns', + traceEvents: performance.getEntriesByType('measure').map(({ name, startTime, duration, detail }) => ({ + name, + cat: (detail as { cat: string })?.cat, + ph: 'X', + ts: startTime * 1000, + dur: duration * 1000, + args: detail, + })) + })); + } } diff --git a/src/index.ts b/src/index.ts index 9118601..3f5e032 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,10 @@ -import { dump, log } from './debug'; +import { dump, log, profile } from './debug'; import { _ } from './core'; import { fromNumber, toNumber } from './num'; import { Cons, Nil, toArray } from './list'; log(); +profile(); const program = Cons._(fromNumber(0))._(Nil); diff --git a/src/list.show.ts b/src/list.show.ts index d12d820..0c80749 100644 --- a/src/list.show.ts +++ b/src/list.show.ts @@ -1,7 +1,7 @@ -import { _, g, l, m } from './core'; +import { _, g, l, n } from './core'; import { concat, intersperse, map } from './list'; import { fromString as _s, cons, snoc } from './string'; -m('list.show'); +n('list.show'); export const show = g('show', l('eshow', l('xs', concat._(snoc._(cons._(_s('['))._(intersperse._(_s(', '))._(map._('eshow')._('xs'))))._(_s(']')))))); diff --git a/src/list.ts b/src/list.ts index 4fede68..0e56e42 100644 --- a/src/list.ts +++ b/src/list.ts @@ -1,9 +1,9 @@ -import { $, Term, _, cnst, d, g, id, l, m, pipe, r, undef } from './core'; +import { $, Term, cnst, _, g, id, l, n, pipe, r, undef } from './core'; import { EQ, GT, LT, eq as eqc } from './ord'; import { False, True, and, iif, or } from './bool'; import { Zero, succ } from './num'; -m('list'); +n('list'); export const Nil = g('Nil', l('z', l('_', 'z'))); @@ -11,62 +11,62 @@ export const Cons = g('Cons', l('x', l('xs', l('_', l('f', r('f')._('x')._('xs') export const cons = g('cons', Cons); -export const snoc = g('snoc', l('xs', l('x', r('xs')._(cons._('x')._(Nil))._(l('y', l('ys', cons._('y')._(r('list::snoc')._('ys')._('x')))))))); +export const snoc = g('snoc', l('xs', l('x', r('xs')._(cons._('x')._(Nil))._(l('y', l('ys', cons._('y')._(r('list', 'snoc')._('ys')._('x')))))))); export const nul = g('nul', l('xs', r('xs')._(True)._(cnst._(cnst._(False))))); -export const len = g('len', l('xs', r('xs')._(Zero)._(cnst._(pipe._(succ)._(r('list::len')))))); +export const len = g('len', l('xs', r('xs')._(Zero)._(cnst._(pipe._(succ)._(r('list', 'len')))))); export const singleton = g('singleton', l('x', cons._('x')._(Nil))); -export const append = g('append', l('l', l('r', r('l')._('r')._(l('x', l('xs', cons._('x')._(r('list::append')._('xs')._('r')))))))); +export const append = g('append', l('l', l('r', r('l')._('r')._(l('x', l('xs', cons._('x')._(r('list', 'append')._('xs')._('r')))))))); -export const concat = g('concat', l('ls', r('ls')._(Nil)._(l('x', pipe._(append._('x'))._(r('list::concat')))))); +export const concat = g('concat', l('ls', r('ls')._(Nil)._(l('x', pipe._(append._('x'))._(r('list', 'concat')))))); -export const repeat = g('repeat', l('x', cons._('x')._(r('list::repeat')._('x')))); +export const repeat = g('repeat', l('x', cons._('x')._(r('list', 'repeat')._('x')))); -export const iterate = g('iterate', l('f', l('x', cons._('x')._(r('list::iterate')._('f')._(r('f')._('x')))))); +export const iterate = g('iterate', l('f', l('x', cons._('x')._(r('list', 'iterate')._('f')._(r('f')._('x')))))); export const head = g('head', l('xs', r('xs')._(undef)._(cnst))); export const tail = g('tail', l('xs', r('xs')._(undef)._(cnst._(id)))); -export const foldl = g('foldl', l('f', l('z', l('xs', r('xs')._('z')._(pipe._(r('list::foldl')._('f'))._(r('f')._('z'))))))); +export const foldl = g('foldl', l('f', l('z', l('xs', r('xs')._('z')._(pipe._(r('list', 'foldl')._('f'))._(r('f')._('z'))))))); export const foldlz = g('foldlz', l('f', l('xs', r('xs')._(undef)._(foldl._('f'))))); -export const foldr = g('foldr', l('f', l('z', l('xs', r('xs')._('z')._(l('x', pipe._(r('f')._('x'))._(r('list::foldr')._('f')._('z')))))))); +export const foldr = g('foldr', l('f', l('z', l('xs', r('xs')._('z')._(l('x', pipe._(r('f')._('x'))._(r('list', 'foldr')._('f')._('z')))))))); export const foldrz = g('foldrz', undef); -export const cmp = g('cmp', l('ecmp', l('l', l('r', r('l')._(r('r')._(EQ)._(cnst._(cnst._(LT))))._(l('lx', l('lxs', r('r')._(GT)._(l('rx', l('rxs', iif._(eqc._(EQ)._(r('ecmp')._('lx')._('rx')))._(r('list::cmp')._('ecmp')._('lxs')._('rxs'))._(r('ecmp')._('lx')._('rx')))))))))))); +export const cmp = g('cmp', l('ecmp', l('l', l('r', r('l')._(r('r')._(EQ)._(cnst._(cnst._(LT))))._(l('lx', l('lxs', r('r')._(GT)._(l('rx', l('rxs', iif._(eqc._(EQ)._(r('ecmp')._('lx')._('rx')))._(r('list', 'cmp')._('ecmp')._('lxs')._('rxs'))._(r('ecmp')._('lx')._('rx')))))))))))); -export const lt = g('lt', l('ecmp', l('l', l('r', r('r')._(False)._(l('rx', l('rxs', r('l')._(True)._(l('lx', l('lxs', r('ecmp')._('lx')._('rx')._(True)._(r('list::lt')._('ecmp')._('lxs')._('rxs'))._(False))))))))))); +export const lt = g('lt', l('ecmp', l('l', l('r', r('r')._(False)._(l('rx', l('rxs', r('l')._(True)._(l('lx', l('lxs', r('ecmp')._('lx')._('rx')._(True)._(r('list', 'lt')._('ecmp')._('lxs')._('rxs'))._(False))))))))))); -export const le = g('le', l('ecmp', l('l', l('r', r('l')._(True)._(l('lx', l('lxs', r('r')._(False)._(l('rx', l('rxs', r('ecmp')._('lx')._('rx')._(True)._(r('list::le')._('ecmp')._('lxs')._('rxs'))._(False))))))))))); +export const le = g('le', l('ecmp', l('l', l('r', r('l')._(True)._(l('lx', l('lxs', r('r')._(False)._(l('rx', l('rxs', r('ecmp')._('lx')._('rx')._(True)._(r('list', 'le')._('ecmp')._('lxs')._('rxs'))._(False))))))))))); -export const eq = g('eq', l('eeq', l('l', l('r', r('l')._(r('r')._(True)._(cnst._(cnst._(False))))._(l('lx', l('lxs', r('r')._(False)._(l('rx', l('rxs', and._(r('eeq')._('lx')._('rx'))._(r('list::eq')._('eeq')._('lxs')._('rxs')))))))))))); +export const eq = g('eq', l('eeq', l('l', l('r', r('l')._(r('r')._(True)._(cnst._(cnst._(False))))._(l('lx', l('lxs', r('r')._(False)._(l('rx', l('rxs', and._(r('eeq')._('lx')._('rx'))._(r('list', 'eq')._('eeq')._('lxs')._('rxs')))))))))))); -export const ge = g('ge', l('ecmp', l('l', l('r', r('r')._(True)._(l('rx', l('rxs', r('l')._(False)._(l('lx', l('lxs', r('ecmp')._('lx')._('rx')._(False)._(r('list::ge')._('ecmp')._('lxs')._('rxs'))._(True))))))))))); +export const ge = g('ge', l('ecmp', l('l', l('r', r('r')._(True)._(l('rx', l('rxs', r('l')._(False)._(l('lx', l('lxs', r('ecmp')._('lx')._('rx')._(False)._(r('list', 'ge')._('ecmp')._('lxs')._('rxs'))._(True))))))))))); -export const gt = g('gt', l('ecmp', l('l', l('r', r('l')._(False)._(l('lx', l('lxs', r('r')._(True)._(l('rx', l('rxs', r('ecmp')._('lx')._('rx')._(False)._(r('list::gt')._('ecmp')._('lxs')._('rxs'))._(True))))))))))); +export const gt = g('gt', l('ecmp', l('l', l('r', r('l')._(False)._(l('lx', l('lxs', r('r')._(True)._(l('rx', l('rxs', r('ecmp')._('lx')._('rx')._(False)._(r('list', 'gt')._('ecmp')._('lxs')._('rxs'))._(True))))))))))); -export const map = g('map', l('f', l('xs', r('xs')._(Nil)._(l('x', pipe._(cons._(r('f')._('x')))._(r('list::map')._('f'))))))); +export const map = g('map', l('f', l('xs', r('xs')._(Nil)._(l('x', pipe._(cons._(r('f')._('x')))._(r('list', 'map')._('f'))))))); -export const filter = g('filter', l('f', l('xs', r('xs')._(Nil)._(l('x', pipe._(iif._(r('f')._('x'))._(cons._('x'))._(id))._(r('list::filter')._('f'))))))); +export const filter = g('filter', l('f', l('xs', r('xs')._(Nil)._(l('x', pipe._(iif._(r('f')._('x'))._(cons._('x'))._(id))._(r('list', 'filter')._('f'))))))); -export const any = g('any', l('f', l('xs', r('xs')._(False)._(l('x', pipe._(or._(r('f')._('x')))._(r('list::any')._('f'))))))); +export const any = g('any', l('f', l('xs', r('xs')._(False)._(l('x', pipe._(or._(r('f')._('x')))._(r('list', 'any')._('f'))))))); -export const all = g('all', l('f', l('xs', r('xs')._(True)._(l('x', pipe._(and._(r('f')._('x')))._(r('list::all')._('f'))))))); +export const all = g('all', l('f', l('xs', r('xs')._(True)._(l('x', pipe._(and._(r('f')._('x')))._(r('list', 'all')._('f'))))))); -export const get = g('get', l('i', l('xs', r('xs')._(undef)._(l('x', r('i')._(cnst._('x'))._('list::get')))))); +export const get = g('get', l('i', l('xs', r('xs')._(undef)._(l('x', r('i')._(cnst._('x'))._('list', 'get')))))); -export const update = g('update', l('f', l('i', l('xs', r('xs')._(undef)._(l('x', l('xs', r('i')._(cons._(r('f')._('x'))._('xs'))._(l('pi', cons._('x')._(r('list::update')._('f')._('pi')._('xs'))))))))))); +export const update = g('update', l('f', l('i', l('xs', r('xs')._(undef)._(l('x', l('xs', r('i')._(cons._(r('f')._('x'))._('xs'))._(l('pi', cons._('x')._(r('list', 'update')._('f')._('pi')._('xs'))))))))))); export const set = g('set', pipe._(update)._(cnst)); -export const intersperse = g('intersperse', l('v', l('xs', r('xs')._('xs')._(l('x', l('rxs', r('rxs')._('xs')._(cnst._(cnst._(cons._('x')._(cons._('v')._(r('list::intersperse')._('v')._('rxs')))))))))))); +export const intersperse = g('intersperse', l('v', l('xs', r('xs')._('xs')._(l('x', l('rxs', r('rxs')._('xs')._(cnst._(cnst._(cons._('x')._(cons._('v')._(r('list', 'intersperse')._('v')._('rxs')))))))))))); -export const fromArray: (xs: Term[]) => Term = xs => !xs.length ? Nil : Cons._(xs[0])._(d(() => fromArray(xs.slice(1)))); +export const fromArray: (xs: Term[]) => Term = xs => !xs.length ? Nil : Cons._(xs[0])._(_(() => fromArray(xs.slice(1)))); -export const toArray: (xs: Term) => Term[] = xs => $(xs._(_([]))._(l('x', l('xs', d(bs => _([bs['x'], ...toArray(bs['xs'])])))))); +export const toArray: (xs: Term) => Term[] = xs => $(xs._(_([]))._(l('x', l('xs', _(({ x, xs }) => _([x, ...toArray(xs)])))))); diff --git a/src/log.ts b/src/log.ts index a3e8bbc..67435b4 100644 --- a/src/log.ts +++ b/src/log.ts @@ -1,7 +1,7 @@ -import { l, d } from './core'; +import { l, _ } from './core'; import { toString } from './string'; -export const log = l('s', l('p', d(bs => { - console.log(toString(bs['s'])); - return bs['p']; +export const log = l('s', l('p', _(({ s, p }) => { + console.log(toString(s)); + return p; }))); diff --git a/src/num.show.ts b/src/num.show.ts index 06bef97..b628919 100644 --- a/src/num.show.ts +++ b/src/num.show.ts @@ -1,8 +1,8 @@ -import { _, g, l, m, r } from './core'; +import { _, g, l, n, r } from './core'; import { iif } from './bool'; import { fromNumber as _n, sum, div, mod, lt } from './num'; import { append, cons, Empty } from './string'; -m('num.show'); +n('num.show'); -export const show = g('show', l('n', iif._(lt._('n')._(_n(10)))._(cons._(sum._('n')._(_n(48)))._(Empty))._(append._(r('num.show::show')._(div._('n')._(_n(10))))._(r('num.show::show')._(mod._('n')._(_n(10))))))); +export const show = g('show', l('n', iif._(lt._('n')._(_n(10)))._(cons._(sum._('n')._(_n(48)))._(Empty))._(append._(r('num.show', 'show')._(div._('n')._(_n(10))))._(r('num.show', 'show')._(mod._('n')._(_n(10))))))); diff --git a/src/num.ts b/src/num.ts index d6b6d94..56f3a6a 100644 --- a/src/num.ts +++ b/src/num.ts @@ -1,8 +1,8 @@ -import { $, Term, _, cnst, d, g, id, l, m, pipe, r } from './core'; +import { $, Term, cnst, _, g, id, l, n, pipe, r } from './core'; import { EQ, GT, LT } from './ord'; import { False, True, iif } from './bool'; -m('num'); +n('num'); export const Zero = g('Zero', l('z', l('_n', 'z'))); @@ -14,32 +14,32 @@ export const succ = g('succ', Succ); export const pred = g('pred', l('n', r('n')._(Zero)._(id))); -export const cmp = g('cmp', l('l', l('r', r('l')._(r('r')._(EQ)._(cnst._(LT)))._(pipe._(r('r')._(GT))._('num::cmp'))))); +export const cmp = g('cmp', l('l', l('r', r('l')._(r('r')._(EQ)._(cnst._(LT)))._(pipe._(r('r')._(GT))._('num', 'cmp'))))); -export const lt = g('lt', l('l', l('r', r('r')._(False)._(r('l')._(cnst._(True))._('num::lt'))))); +export const lt = g('lt', l('l', l('r', r('r')._(False)._(r('l')._(cnst._(True))._('num', 'lt'))))); -export const le = g('le', l('l', l('r', r('l')._(True)._(r('r')._(cnst._(False))._('num::ge'))))); +export const le = g('le', l('l', l('r', r('l')._(True)._(r('r')._(cnst._(False))._('num', 'ge'))))); -export const eq = g('eq', l('l', l('r', r('l')._(r('r')._(True)._(cnst._(False)))._(r('r')._(cnst._(False))._('num::eq'))))); +export const eq = g('eq', l('l', l('r', r('l')._(r('r')._(True)._(cnst._(False)))._(r('r')._(cnst._(False))._('num', 'eq'))))); -export const ge = g('ge', l('l', l('r', r('r')._(True)._(r('l')._(cnst._(False))._('num::ge'))))); +export const ge = g('ge', l('l', l('r', r('r')._(True)._(r('l')._(cnst._(False))._('num', 'ge'))))); export const gt = g('gt', l('l', l('r', r('l')._(False)._(r('r')._(cnst._(True))._(lt))))); -export const even = g('even', l('n', r('n')._(True)._('num::odd'))); +export const even = g('even', l('n', r('n')._(True)._('num', 'odd'))); -export const odd = g('odd', l('n', r('n')._(False)._('num::even'))); +export const odd = g('odd', l('n', r('n')._(False)._('num', 'even'))); -export const sum = g('sum', l('l', l('r', r('r')._('l')._(r('num::sum')._(Succ._('l')))))); +export const sum = g('sum', l('l', l('r', r('r')._('l')._(r('num', 'sum')._(Succ._('l')))))); -export const sub = g('sub', l('l', l('r', r('r')._('l')._(r('l')._(cnst._(Zero))._('num::sub'))))); +export const sub = g('sub', l('l', l('r', r('r')._('l')._(r('l')._(cnst._(Zero))._('num', 'sub'))))); -export const mul = g('mul', l('l', l('r', r('l')._(Zero)._(l('pl', sum._('r')._(r('num::mul')._('pl')._('r'))))))); +export const mul = g('mul', l('l', l('r', r('l')._(Zero)._(l('pl', sum._('r')._(r('num', 'mul')._('pl')._('r'))))))); -export const div = g('div', l('l', l('r', iif._(lt._('l')._('r'))._(Zero)._(succ._(r('num::div')._(sub._('l')._('r'))._('r')))))); +export const div = g('div', l('l', l('r', iif._(lt._('l')._('r'))._(Zero)._(succ._(r('num', 'div')._(sub._('l')._('r'))._('r')))))); -export const mod = g('mod', l('l', l('r', iif._(lt._('l')._('r'))._('l')._(r('num::mod')._(sub._('l')._('r'))._('r'))))); +export const mod = g('mod', l('l', l('r', iif._(lt._('l')._('r'))._('l')._(r('num', 'mod')._(sub._('l')._('r'))._('r'))))); -export const fromNumber: (n: number) => Term = n => !n ? Zero : Succ._(d(() => fromNumber(n - 1))); +export const fromNumber: (n: number) => Term = n => !n ? Zero : Succ._(_(() => fromNumber(n - 1))); -export const toNumber: (n: Term) => number = n => $(n._(_(0))._(l('p', d(bs => _(1 + toNumber(bs['p'])))))); +export const toNumber: (n: Term) => number = n => $(n._(_(0))._(l('p', _(({ p }) => _(1 + toNumber(p)))))); diff --git a/src/ord.show.ts b/src/ord.show.ts index 204cd1f..0b4dfa1 100644 --- a/src/ord.show.ts +++ b/src/ord.show.ts @@ -1,6 +1,6 @@ -import { _, g, l, m, r } from './core'; +import { _, g, l, n, r } from './core'; import { fromString as _s } from './string'; -m('ord.show'); +n('ord.show'); export const show = g('show', l('o', r('o')._(_s('LT'))._(_s('EQ'))._(_s('GT')))); diff --git a/src/ord.ts b/src/ord.ts index 2cdceed..cda20b3 100644 --- a/src/ord.ts +++ b/src/ord.ts @@ -1,6 +1,6 @@ -import { $, Term, _, c, g, l, m, r } from './core'; +import { $, Term, _, g, l, n, r } from './core'; -m('ord'); +n('ord'); export const enum NOrd { LT, @@ -14,13 +14,13 @@ export const EQ = g('EQ', l('_', l('eq', l('_', 'eq')))); export const GT = g('GT', l('_', l('_', l('gt', 'gt')))); -export const eq = g('eq', l('l', l('r', c('l', - c('r', 'bool::True', 'bool::False', 'bool::False'), - c('r', 'bool::False', 'bool::True', 'bool::False'), - c('r', 'bool::False', 'bool::False', 'bool::True'))))); +export const eq = g('eq', l('l', l('r', r('l') + ._(r('r')._('bool', 'True')._('bool', 'False')._('bool', 'False')) + ._(r('r')._('bool', 'False')._('bool', 'True')._('bool', 'False')) + ._(r('r')._('bool', 'False')._('bool', 'False')._('bool', 'True'))))); export const fromNOrd: (o: NOrd) => Term = o => o === NOrd.LT ? LT : o === NOrd.EQ ? EQ : GT; export const toNOrd: (o: Term) => NOrd - = o => $(c(o, _(NOrd.LT), _(NOrd.EQ), _(NOrd.GT)).reduce()); + = o => $(o._(_(NOrd.LT))._(_(NOrd.EQ))._(_(NOrd.GT)).reduce()); diff --git a/src/pair.show.ts b/src/pair.show.ts index 92719a5..d09cbf3 100644 --- a/src/pair.show.ts +++ b/src/pair.show.ts @@ -1,8 +1,8 @@ -import { _, g, l, m, r } from './core'; +import { _, g, l, n, r } from './core'; import { uncurry } from './pair'; import { concat, cons, Nil } from './list'; import { fromString as _s } from './string'; -m('pair.show'); +n('pair.show'); export const show = g('show', l('fshow', l('sshow', uncurry._(l('f', l('s', concat._(cons._(_s('('))._(cons._(r('fshow')._('f'))._(cons._(_s(', '))._(cons._(r('sshow')._('s'))._(cons._(_s(')'))._(Nil)))))))))))); diff --git a/src/pair.ts b/src/pair.ts index 9df0f3e..0c33243 100644 --- a/src/pair.ts +++ b/src/pair.ts @@ -1,9 +1,9 @@ -import { _, g, l, m, r } from './core'; +import { _, g, l, n, r } from './core'; import { GT, LT } from './ord'; import { False, True, and } from './bool'; import { fromString as _s } from './string'; -m('pair'); +n('pair'); export const Pair = g('Pair', l('f', l('s', l('p', r('p')._('f')._('s'))))); diff --git a/src/string.show.ts b/src/string.show.ts index c4795af..6ce8c1e 100644 --- a/src/string.show.ts +++ b/src/string.show.ts @@ -1,7 +1,7 @@ -import { _, g, m } from './core'; +import { _, g, n } from './core'; import { fromChar as _c } from './char'; import { wrap, fromString as _s } from './string'; -m('string.show'); +n('string.show'); export const show = g('show', wrap._(_s('"'))); diff --git a/src/string.ts b/src/string.ts index 690b83e..b6ef213 100644 --- a/src/string.ts +++ b/src/string.ts @@ -1,10 +1,10 @@ -import { $, Term, _, d, g, l, m } from './core'; +import { $, Term, _, g, l, n } from './core'; import { fromChar as _c, cmp as cmpc, eq as eqc, fromChar, toChar } from './char'; import { Cons, Nil, append, cmp as cmpl, eq as eql, ge as gel, gt as gtl, le as lel, lt as ltl, nul } from './list'; export { len, Cons, cons, snoc, append, repeat, iterate, get, update, set } from './list'; -m('string'); +n('string'); export const Empty = g('Empty', Nil); @@ -24,6 +24,6 @@ export const gt = g('gt', gtl._(cmpc)); export const wrap = g('wrap', l('w', l('c', append._('w')._(append._('c')._('w'))))); -export const fromString: (xs: string) => Term = s => !s.length ? Nil : Cons._(fromChar(s[0]))._(d(() => fromString(s.slice(1)))); +export const fromString: (xs: string) => Term = s => !s.length ? Nil : Cons._(fromChar(s[0]))._(_(() => fromString(s.slice(1)))); -export const toString: (xs: Term) => string = s => $(s._(_(''))._(l('x', l('xs', d(bs => _(toChar(bs['x']) + toString(bs['xs']))))))); +export const toString: (xs: Term) => string = s => $(s._(_(''))._(l('x', l('xs', _(({ x, xs }) => _(toChar(x) + toString(xs))))))); diff --git a/test/list.spec.ts b/test/list.spec.ts index cf4041c..28c2d87 100644 --- a/test/list.spec.ts +++ b/test/list.spec.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from '@jest/globals'; -import { $, Term, _, d, g, l, m, undef } from '../src/core'; +import { $, Term, _, g, l, n, undef } from '../src/core'; import { toNOrd as $o, NOrd } from '../src/ord'; import { toBoolean as $b, iif } from '../src/bool'; import { toNumber as $n, Succ, Zero, fromNumber as _n, fromNumber as _nn, cmp as cmpn, eq as eqn, even, gt as ngt, odd, succ, sum, } from '../src/num'; @@ -9,14 +9,14 @@ import { toString as $s } from '../src/string'; import { show as nshow } from '../src/num.show'; import { show } from '../src/list.show'; -m('list.spec'); +n('list.spec'); describe('List', () => { const infinite: Term = g('infinite', iterate._(Succ)._(Zero)); - const _na: (xs: number[]) => Term = xs => !xs.length ? Nil : Cons._(_n(xs[0]))._(d(() => _na(xs.slice(1)))); + const _na: (xs: number[]) => Term = xs => !xs.length ? Nil : Cons._(_n(xs[0]))._(_(() => _na(xs.slice(1)))); - const $na: (xs: Term) => number[] = xs => $(xs._(_([]))._(l('x', l('xs', d(bs => _([$n(bs['x']), ...$na(bs['xs'])])))))); + const $na: (xs: Term) => number[] = xs => $(xs._(_([]))._(l('x', l('xs', _(({ x, xs }) => _([$n(x), ...$na(xs)])))))); describe('toArray', () => { it('converts []', () => { diff --git a/test/num.spec.ts b/test/num.spec.ts index 1e4aae8..0b61a72 100644 --- a/test/num.spec.ts +++ b/test/num.spec.ts @@ -1,15 +1,15 @@ import { describe, expect, it } from '@jest/globals'; -import { Term, d, g, m, undef } from '../src/core'; +import { Term, _, g, n, undef } from '../src/core'; import { toNOrd as $o, NOrd } from '../src/ord'; import { toBoolean as $b, fromBoolean as _b } from '../src/bool'; -import { toNumber as $n, Succ, Zero, fromNumber as _n, cmp, div, eq, ge, gt, ifz, le, lt, mod, mul, pred, sub, succ, sum } from '../src/num'; +import { toNumber as $n, Succ, Zero, fromNumber as _n, cmp, div, eq, ge, gt, ifz, le, lt, mod, mul, pred, sub, succ, sum } from '../src/num'; import { toString as $s } from '../src/string'; import { show } from '../src/num.show'; -m('num.spec'); +n('num.spec'); describe('Num', () => { - const infinity: Term = g('infinity', d(bs => Succ._(bs['num.spec::infinity']))); + const infinity: Term = g('infinity', _(() => Succ._('num.spec', 'infinity'))); describe('toNumber', () => { it('converts 0', () => { diff --git a/test/string.spec.ts b/test/string.spec.ts index 9de3fd0..c0c7b4b 100644 --- a/test/string.spec.ts +++ b/test/string.spec.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from '@jest/globals'; -import { Term, _, g, l, m, undef } from '../src/core'; +import { Term, _, g, l, n, undef } from '../src/core'; import { NOrd, toNOrd } from '../src/ord'; import { toBoolean as $b } from '../src/bool'; import { toNumber as $n, Zero, gt as ngt } from '../src/num'; @@ -7,10 +7,10 @@ import { toChar as $c, fromChar as _c } from '../src/char'; import { toString as $s, Cons, Empty, fromString as _s, append, cmp, empty, eq, ge, gt, le, len, lt, wrap } from '../src/string'; import { show } from '../src/string.show'; -m('string.spec'); +n('string.spec'); describe('String', () => { - const infinite: Term = g('infinite', Cons._(_c('a'))._('string.spec::infinite')); + const infinite: Term = g('infinite', Cons._(_c('a'))._('string.spec', 'infinite')); describe('toString', () => { it('converts ""', () => {