Some optimizations

master
Freywar Ulvnaudgari 2 years ago
parent f836e388d7
commit d7a91ad6fd
  1. 3
      eslint.config.mjs
  2. 1
      package.json
  3. 1
      profile.json
  4. 2
      src/bool.show.ts
  5. 2
      src/char.show.ts
  6. 3
      src/char.ts
  7. 184
      src/core.ts
  8. 10
      src/debug.ts
  9. 7
      src/index.ts
  10. 2
      src/list.ts
  11. 2
      src/log.ts
  12. 4
      src/num.show.ts
  13. 2
      src/num.ts
  14. 2
      src/pair.show.ts
  15. 2
      src/string.show.ts
  16. 2
      test/list.spec.ts
  17. 64
      yarn.lock

@ -4,6 +4,7 @@ import { FlatCompat } from '@eslint/eslintrc'
import js from '@eslint/js'
import ts from '@typescript-eslint/eslint-plugin'
import stylistic from '@stylistic/eslint-plugin'
import stylisticts from '@stylistic/eslint-plugin-ts'
import parser from '@typescript-eslint/parser'
export default [
@ -24,6 +25,7 @@ export default [
plugins: {
'@typescript-eslint': ts,
'@stylistic': stylistic,
'@stylistic/ts': stylisticts,
},
rules: {
'arrow-body-style': 'error',
@ -33,6 +35,7 @@ export default [
'@stylistic/brace-style': ['error', '1tbs', { allowSingleLine: true }],
'@stylistic/quotes': ['error', 'single'],
'@stylistic/semi': ['error', 'always'],
'@stylistic/ts/semi': ['error', 'always'],
'@stylistic/indent': 'error',
'@stylistic/arrow-parens': ['error', 'as-needed', { requireForBlockBody: false }],
'@stylistic/max-statements-per-line': 'off',

@ -19,6 +19,7 @@
"@eslint/js": "^9.8.0",
"@jest/globals": "^29.7.0",
"@stylistic/eslint-plugin": "^2.6.1",
"@stylistic/eslint-plugin-ts": "^2.6.4",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"eslint": "^9.8.0",

File diff suppressed because one or more lines are too long

@ -1,4 +1,4 @@
import { n, g, l } from './core';
import { g, l, n } from './core';
import { iif } from './bool';
import { fromString as _s } from './string';

@ -1,6 +1,6 @@
import { g, l, n, pipe } from './core';
import { singleton } from './list';
import { wrap, fromString as _s } from './string';
import { fromString as _s, wrap } from './string';
n('char.show');

@ -1,6 +1,5 @@
import { Term, g, l, n, pipe } from './core';
import { Term, g, n } from './core';
import { toNumber as $n, fromNumber as _n, x00 } from './byte';
import { singleton } from './list';
n('char');

@ -1,16 +1,34 @@
import { withLog, withProfile } from './debug';
import { withProfile as simpleWithProfile, withLog } from './debug';
const withProfile = (term: Term, action: string, func: () => Term) => simpleWithProfile(
`${term.expression()}${term.cache ? ' [cached]' : ''}`,
{
cat: action,
id: term.id,
bindings: Object.fromEntries(Object.entries(term.bindings).map(([k, v]) => [k, v.toString()])),
},
func,
);
let index = 0;
export abstract class Term {
protected id = index++;
protected cache: Term | null = null;
public id = index++;
public cache: Term | null = null;
public constructor(protected bindings: Record<string, Term>) { }
public constructor() { }
public refs(_name: string): boolean {
return false;
};
}
public bound(_name: string): boolean {
return false;
}
public get bindings(): Record<string, Term> {
return {};
}
public bind(_name: string, _term: Term): Term {
return this;
@ -49,29 +67,29 @@ export abstract class Term {
}
type Termify = {
(term: Term): Term;
(name: string): Term;
(namespace: string, name: string): Term;
}
(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() { super({}); }
constructor() { super(); }
override reduce(): Term {
throw new Error(`undef`);
throw new Error('undef');
}
}
export class Native extends Term {
constructor(
public constructor(
public value: any,
) { super({}); }
) { super(); }
public override expression(): string {
return `\`${JSON.stringify(this.value)}\``
return `\`${JSON.stringify(this.value)}\``;
}
public override stringify(): string {
@ -80,17 +98,25 @@ export class Native extends Term {
}
export class Deferred extends Term {
constructor(
public func: (bindings: Record<string, Term>) => Term,
bindings: Record<string, Term> = {},
) { super(bindings); }
public constructor(
protected func: (bindings: Record<string, Term>) => Term,
protected _bindings: Record<string, Term> = {},
) { super(); }
override refs(_name: string): boolean {
public override refs(_name: string): boolean {
return true;
}
public override bound(name: string): boolean {
return !!this._bindings[name];
}
public override get bindings(): Record<string, Term> {
return this._bindings;
}
public override bind(name: string, term: Term): Term {
return this.bindings[name] || !this.refs(name)
return this.bound(name) || !this.refs(name)
? this
: new Deferred(this.func, { ...this.bindings, [name]: term });
}
@ -99,8 +125,8 @@ export class Deferred extends Term {
return this.cache = withLog(
`Executing ${this.stringify()}`,
() => withProfile(
`${this.expression()}${this.cache ? ' [cached]' : ''}`,
{ cat: 'deferred' },
this,
'deferred',
() => this.cache ?? Object.entries(this.bindings).reduce((t, b) => t.bind(...b), this.func(this.bindings)).reduce(),
),
r => `got ${r.stringify()}`,
@ -113,54 +139,69 @@ export class Deferred extends Term {
}
export class Ref extends Term {
constructor(
public namespace: string,
public name: string,
bindings: Record<string, Term> = {},
) { super(bindings); }
public constructor(
protected namespace: string,
protected name: string,
protected binding: Term | null = null,
) { super(); }
override refs(name: string): boolean {
public override refs(name: string): boolean {
return this.name === name;
}
public override bound(name: string): boolean {
return this.name === name && !!this.binding;
}
public override get bindings(): Record<string, Term> {
return this.binding ? { [this.name]: this.binding } : {};
}
public override bind(name: string, term: Term): Term {
return this.bindings[name] || !this.refs(name)
return this.bound(name) || !this.refs(name)
? this
: new Ref(this.namespace, this.name, { [name]: term });
: new Ref(this.namespace, this.name, term);
}
public override reduce(): Term {
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(),
this,
'deref',
() => this.cache ?? ((this.namespace === 'local' ? this.binding : globals[`${this.namespace}::${this.name}`]) ?? new Undef()).reduce(),
),
r => `with ${r.stringify()}`,
);
}
override expression(): string {
public override expression(): string {
return this.name;
}
}
export class Lambda extends Term {
constructor(
public constructor(
public param: string,
public body: Term,
bindings: Record<string, Term> = {},
) { super(bindings); }
) { super(); }
override refs(name: string): boolean {
public override refs(name: string): boolean {
return this.param !== name && this.body.refs(name);
}
override bind(name: string, term: Term): Term {
return this.bindings[name] || !this.refs(name)
public override bound(name: string): boolean {
return this.param !== name && this.body.bound(name);
}
public override get bindings(): Record<string, Term> {
return this.body.bindings;
}
public override bind(name: string, term: Term): Term {
return this.bound(name) || !this.refs(name)
? this
: new Lambda(this.param, this.body.bind(name, term), { ...this.bindings, [name]: term });
: new Lambda(this.param, this.body.bind(name, term));
}
public override expression(): string {
@ -171,49 +212,59 @@ export class Lambda extends Term {
}
export class Call extends Term {
constructor(
public constructor(
public func: Term,
public arg: Term,
bindings: Record<string, Term> = {},
) { super(bindings); }
) { super(); }
override refs(name: string): boolean {
public override refs(name: string): boolean {
return this.func.refs(name) || this.arg.refs(name);
}
override bind(name: string, term: Term): Term {
return this.bindings[name] || !this.refs(name)
public override bound(name: string): boolean {
return this.func.bound(name) || this.arg.bound(name);
}
override get bindings(): Record<string, Term> {
return { ...this.func.bindings, ...this.arg.bindings };
}
public override bind(name: string, term: Term): Term {
return this.bound(name) || !this.refs(name)
? this
: new Call(this.func.bind(name, term), this.arg.bind(name, term), { ...this.bindings, [name]: term });
: new Call(this.func.bind(name, term), this.arg.bind(name, term));
}
override reduce(): Term {
public override reduce(): Term {
return this.cache = withLog(
`Reducing ${this.stringify()}`,
() => withProfile(
`${this.expression()}${this.cache ? ' [cached]' : ''}`,
{ cat: 'call' },
() => this.cache ?? (() => {
this,
'call',
() => {
if (this.cache) {
return this.cache;
}
const func = this.func.reduce();
if (!(func instanceof Lambda)) {
return new Undef();
}
return withLog(
`Binding ${this.arg.stringify()} to ${func.param}`,
() => withProfile(
`${func.param} = ${this.arg.expression()}`,
() => simpleWithProfile(
`${func.param} = ${this.arg}`,
{ cat: 'binding' },
() => func.body.bind(func.param, this.arg),
),
r => `got ${r.stringify()}`,
).reduce();
})(),
},
),
r => `to ${r.stringify()}`,
);
}
override expression(): string {
public 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()}`;
}
}
@ -234,26 +285,25 @@ export const g = (name: string, term: Term) => {
};
type Refify = {
(name: string): Ref;
(namespace: string, name: string): Ref;
}
(name: string): Ref
(namespace: string, name: string): Ref
};
export const r: Refify = (namespaceOrName: string, name?: string) => new Ref(name ? namespaceOrName : 'local', name ?? namespaceOrName);
type Lambdify = {
(param: string, body: Term): Lambda;
(param: string, ref: string): Lambda;
}
(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<string, Term>) => Term): Deferred;
(value: boolean | number | string | object): Native;
}
(value: (bindings: Record<string, Term>) => 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 _: Deferedify = ((value: any) => typeof value === 'function' ? new Deferred(value) : new Native(value)) as Deferedify;
export const $ = <T>(t: Term): T => {
const rt = t.reduce();
return rt instanceof Native ? rt.value : rt.stringify();

@ -8,7 +8,6 @@ export function log(): void {
logging = true;
}
export function withLog<T>(before: string, f: () => T, after: (v: T) => string): T {
if (!logging) {
return f();
@ -27,14 +26,13 @@ export function withLog<T>(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<T>(name: string, detail: Record<'cat', string>, f: () => T) {
export function withProfile<T>(name: string, detail: Record<'cat', string> & Record<string, unknown>, f: () => T) {
if (!logging) {
return f();
}
@ -42,11 +40,11 @@ export function withProfile<T>(name: string, detail: Record<'cat', string>, f: (
const i = index++;
performance.mark(`${name}::${i}::start`, { detail });
const result = f();
performance.mark(`${name}::${i}::end`, { detail });
performance.mark(`${name}::${i}::end`, { detail: { ...detail, result: `${result}` } });
performance.measure(name, {
start: `${name}::${i}::start`,
end: `${name}::${i}::end`,
detail,
detail: { ...detail, result: `${result}` },
});
return result;
}
@ -68,7 +66,7 @@ export function dump() {
ts: startTime * 1000,
dur: duration * 1000,
args: detail,
}))
})),
}));
}
}

@ -1,15 +1,14 @@
import { dump, log, profile } from './debug';
import { _ } from './core';
import { fromNumber, toNumber } from './num';
import { Cons, Nil, toArray } from './list';
import { fromNumber, sum, toNumber } from './byte';
log();
profile();
const program = Cons._(fromNumber(0))._(Nil);
const program = sum._(fromNumber(10))._(fromNumber(10));
try {
console.log(program.stringify(), ' -> ', toArray(program).map(toNumber));
console.log(program.stringify(), ' -> ', toNumber(program));
} finally {
dump();
}

@ -1,4 +1,4 @@
import { $, Term, cnst, _, g, id, l, n, 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';

@ -1,4 +1,4 @@
import { l, _ } from './core';
import { _, l } from './core';
import { toString } from './string';
export const log = l('s', l('p', _(({ s, p }) => {

@ -1,7 +1,7 @@
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';
import { fromNumber as _n, div, lt, mod, sum } from './num';
import { Empty, append, cons } from './string';
n('num.show');

@ -1,4 +1,4 @@
import { $, Term, cnst, _, g, id, l, n, 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';

@ -1,6 +1,6 @@
import { _, g, l, n, r } from './core';
import { uncurry } from './pair';
import { concat, cons, Nil } from './list';
import { Nil, concat, cons } from './list';
import { fromString as _s } from './string';
n('pair.show');

@ -1,6 +1,6 @@
import { _, g, n } from './core';
import { fromChar as _c } from './char';
import { wrap, fromString as _s } from './string';
import { fromString as _s, wrap } from './string';
n('string.show');

@ -2,7 +2,7 @@ import { describe, expect, it } from '@jest/globals';
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';
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';
import { toArray as $a, Cons, Nil, fromArray as _a, all, any, append, cmp, concat, cons, eq, filter, foldl, foldlz, foldr, foldrz, ge, get, gt, head, intersperse, iterate, le, len, lt, map, nul, set, singleton, snoc, tail, update } from '../src/list';
import { toChar as $c } from '../src/char';
import { toString as $s } from '../src/string';

@ -662,6 +662,16 @@
eslint-visitor-keys "^4.0.0"
espree "^10.1.0"
"@stylistic/eslint-plugin-js@2.6.4":
version "2.6.4"
resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.6.4.tgz#52e2b89e1f0878aaeeca3be832287bc060d3a422"
integrity sha512-kx1hS3xTvzxZLdr/DCU/dLBE++vcP97sHeEFX2QXhk1Ipa4K1rzPOLw1HCbf4mU3s+7kHP5eYpDe+QteEOFLug==
dependencies:
"@types/eslint" "^9.6.0"
acorn "^8.12.1"
eslint-visitor-keys "^4.0.0"
espree "^10.1.0"
"@stylistic/eslint-plugin-jsx@2.6.1":
version "2.6.1"
resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin-jsx/-/eslint-plugin-jsx-2.6.1.tgz#a7ba8242a27a8956455789dfcc087f5231fb4a7c"
@ -689,6 +699,15 @@
"@types/eslint" "^9.6.0"
"@typescript-eslint/utils" "^8.0.0"
"@stylistic/eslint-plugin-ts@^2.6.4":
version "2.6.4"
resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-2.6.4.tgz#06f975ae5f1d866827b2495e0d93b9e005c0579c"
integrity sha512-yxL8Hj6WkObw1jfiLpBzKy5yfxY6vwlwO4miq34ySErUjUecPV5jxfVbOe4q1QDPKemQGPq93v7sAQS5PzM8lA==
dependencies:
"@stylistic/eslint-plugin-js" "2.6.4"
"@types/eslint" "^9.6.0"
"@typescript-eslint/utils" "^8.1.0"
"@stylistic/eslint-plugin@^2.6.1":
version "2.6.1"
resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin/-/eslint-plugin-2.6.1.tgz#6ccddd1ba275cb2407d9abf546982177b872a603"
@ -855,6 +874,14 @@
"@typescript-eslint/types" "8.0.0"
"@typescript-eslint/visitor-keys" "8.0.0"
"@typescript-eslint/scope-manager@8.2.0":
version "8.2.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.2.0.tgz#4a4bd7e7df5522acc8795c3b6f21e8c41b951138"
integrity sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==
dependencies:
"@typescript-eslint/types" "8.2.0"
"@typescript-eslint/visitor-keys" "8.2.0"
"@typescript-eslint/type-utils@8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.0.0.tgz#facecaf0736bfe8394b9290382f300554cf90884"
@ -870,6 +897,11 @@
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.0.0.tgz#7195ea9369fe5ee46b958d7ffca6bd26511cce18"
integrity sha512-wgdSGs9BTMWQ7ooeHtu5quddKKs5Z5dS+fHLbrQI+ID0XWJLODGMHRfhwImiHoeO2S5Wir2yXuadJN6/l4JRxw==
"@typescript-eslint/types@8.2.0":
version "8.2.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.2.0.tgz#dfe9895a2812f7c6bf7af863054c22a67060420c"
integrity sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==
"@typescript-eslint/typescript-estree@8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0.tgz#d172385ced7cb851a038b5c834c245a97a0f9cf6"
@ -884,6 +916,20 @@
semver "^7.6.0"
ts-api-utils "^1.3.0"
"@typescript-eslint/typescript-estree@8.2.0":
version "8.2.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.2.0.tgz#fbdb93a1c7ac7f1f96ae2de4fc97cd64c60ae894"
integrity sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==
dependencies:
"@typescript-eslint/types" "8.2.0"
"@typescript-eslint/visitor-keys" "8.2.0"
debug "^4.3.4"
globby "^11.1.0"
is-glob "^4.0.3"
minimatch "^9.0.4"
semver "^7.6.0"
ts-api-utils "^1.3.0"
"@typescript-eslint/utils@8.0.0", "@typescript-eslint/utils@^8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.0.0.tgz#1794d6f4b37ec253172a173dc938ae68651b9b99"
@ -894,6 +940,16 @@
"@typescript-eslint/types" "8.0.0"
"@typescript-eslint/typescript-estree" "8.0.0"
"@typescript-eslint/utils@^8.1.0":
version "8.2.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.2.0.tgz#02d442285925f28d520587185f295f932702e733"
integrity sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==
dependencies:
"@eslint-community/eslint-utils" "^4.4.0"
"@typescript-eslint/scope-manager" "8.2.0"
"@typescript-eslint/types" "8.2.0"
"@typescript-eslint/typescript-estree" "8.2.0"
"@typescript-eslint/visitor-keys@8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0.tgz#224a67230190d267e6e78586bd7d8dfbd32ae4f3"
@ -902,6 +958,14 @@
"@typescript-eslint/types" "8.0.0"
eslint-visitor-keys "^3.4.3"
"@typescript-eslint/visitor-keys@8.2.0":
version "8.2.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.2.0.tgz#f6abb3b6508898a117175ddc11f9b9869cc96834"
integrity sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==
dependencies:
"@typescript-eslint/types" "8.2.0"
eslint-visitor-keys "^3.4.3"
acorn-jsx@^5.3.2:
version "5.3.2"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"

Loading…
Cancel
Save