Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

helpers: more helpers #3

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { jam, cue, cue_bytes, bigintToDataView, bi_cut } from "./serial";
import bits from "./bits";
import { dwim, enjs } from "./noun-helpers";
import { putIn, putBy } from "./noun-std";
import { Atom, Cell, Noun } from "./noun";
import compiler from "./compiler";
import { bigIntFromStringWithRadix } from "./bigint";
Expand Down Expand Up @@ -261,4 +262,28 @@ test('dwim <-> enjs.cord', () => {
const cord = enjs.cord(atom);

expect(cord).toEqual(str);
})
});

test('putIn', () => {
const nums = [1, 2, 3, 3, 3, 4, 5, 6, 7, 7, 7];
const ex = [6, [7, [5, 0, 0], 0], 4, [2, [1, 0, 0], 3, 0, 0], 0];

let set: Noun = Atom.zero;
for (let num of nums) {
set = putIn(set, Atom.fromInt(num));
};

expect(set.equals(dwim(ex)));
});

test('putBy', () => {
const nums = [[1, 11], [2, 22], [3, 33], [3, 34], [3, 35], [4, 44], [5, 55], [6, 66], [7, 77], [7, 78], [7, 79]];
const ex = [[6, 66], [[7, 79], [[5, 55], 0, 0], 0], [4, 44], [[2, 22], [[1, 11], 0, 0], [3, 35], 0, 0], 0];

let map: Noun = Atom.zero;
for (let num of nums) {
map = putBy(map, Atom.fromInt(num[0]), Atom.fromInt(num[1]));
};

expect(map.equals(dwim(ex)));
});
80 changes: 80 additions & 0 deletions src/noun-dejs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Atom, Cell, isNoun } from "./noun";
import type { Noun } from "./noun";
import { putIn, putBy } from "./noun-std";

// primitives

type Atomizable = number | string | Atom;

// "Do What I Mean"
function dwim(a: number): Atom;
function dwim(a: string): Atom;
function dwim(a: Atomizable, b: Atomizable): Cell<Atom, Atom>;
function dwim(
a: Atomizable,
b: Atomizable,
c: Atomizable
): Cell<Atom, Cell<Atom, Atom>>;
function dwim(a: Atomizable, ...b: any[]): Cell<Atom, Cell<Noun, Noun>>;
function dwim(...a: any[]): Cell<Noun, Noun>;
// implementation
function dwim(...args: any[]): Noun {
const n = args.length === 1 ? args[0] : args;
if (isNoun(n)) return n;
if (typeof n === "number") {
return Atom.fromInt(n);
} else if (typeof n === "string") {
return Atom.fromCord(n);
} else if (Array.isArray(n)) {
if (n.length < 2) {
return dwim(n[0]);
}
const head = dwim(n[n.length - 2]);
const tail = dwim(n[n.length - 1]);
let cel = new Cell(head, tail);
for (var j = n.length - 3; j >= 0; --j) {
cel = new Cell(dwim(n[j]), cel);
}
return cel;
} else if (n === null) {
return Atom.zero;
}
// objects, undefined, etc
console.error("what do you mean??", typeof n, JSON.stringify(n));
throw new Error('dwim, but meaning unclear');
}

// structures

function list(args: any[]): Noun {
if (args.length === 0) return Atom.zero;
return dwim([...args, Atom.zero]);
}

function set(args: any[]): Noun {
if (args.length === 0) return Atom.zero;
let set: Noun = Atom.zero;
for (let arg of args) {
set = putIn(set, dwim(arg));
}
return set;
}

function map(args: {key: any, val: any}[]): Noun {
if (args.length === 0) return Atom.zero;
let map: Noun = Atom.zero;
for (let arg of args) {
map = putBy(map, dwim(arg.key), dwim(arg.val));
}
return map;
}

const dejs = {
nounify: dwim,
dwim,
list,
set,
map
};

export { dejs, dwim };
204 changes: 204 additions & 0 deletions src/noun-enjs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
import { Atom, Cell } from "./noun";
import type { Noun } from "./noun";
import { bitLength } from "./bigint";

type Json = null | boolean | number | string | Json[] | { [key: string]: Json };
export type EnjsFunction = (n: Noun) => Json;
type frondOpt = { tag: string; get: EnjsFunction };

const frond = function (opts: frondOpt[]): EnjsFunction {
return function (noun) {
if (!(noun instanceof Cell && noun.head instanceof Atom)) {
throw new Error("frond: noun not cell with tag head");
}
const tag = Atom.cordToString(noun.head);
for (let i = 0; i < opts.length; i++) {
if (tag === opts[i].tag) {
return { [tag]: opts[i].get(noun.tail) };
}
}
throw new Error("frond: unknown tag" + tag);
};
};

const tuple = function(funs: EnjsFunction[]): EnjsFunction {
return function (noun) {
let o = [];
while (funs.length > 1) {
if (noun.isAtom()) {
throw new Error("tuple: noun too shallow");
}
o.push(funs[0](noun.head));
funs.splice(0, 1);
noun = noun.tail;
}
o.push(funs[0](noun));
return o;
}
}

type PairCell = { nom: string; get: EnjsFunction };
const pairs = function (cels: PairCell[]): EnjsFunction {
return function (noun) {
let i = 0;
let o: Record<string, Json> = {};
while (i < cels.length - 1) {
if (!(noun instanceof Cell)) {
throw new Error("pairs: noun too shallow");
}
o[cels[i].nom] = cels[i].get(noun.head);
noun = noun.tail;
i++;
}
o[cels[i].nom] = cels[i].get(noun);
return o;
};
};
const pair = function (
na: string,
ga: EnjsFunction,
nb: string,
gb: EnjsFunction
): EnjsFunction {
return pairs([
{ nom: na, get: ga },
{ nom: nb, get: gb },
]);
};

const bucwut = function (opts: EnjsFunction[]): EnjsFunction {
return function (noun) {
for (let i = 0; i < opts.length; i++) {
try {
const res = opts[i](noun);
return res;
} catch (e) {
continue;
}
}
throw new Error("bucwut: no matches");
};
};

// buccen: like frond, but without the wrapper object
const buccen = function (opts: frondOpt[]): EnjsFunction {
return function (noun) {
if (!(noun instanceof Cell && noun.head instanceof Atom)) {
throw new Error("buccen: noun not cell with tag head");
}
const tag = Atom.cordToString(noun.head);
for (let i = 0; i < opts.length; i++) {
if (tag === opts[i].tag) {
return opts[i].get(noun.tail);
}
}
throw new Error("buccen: unknown tag" + tag);
};
};

const array = function (item: EnjsFunction): (n: Noun) => Json[] {
return function (noun) {
let a: Json[] = [];
while (noun instanceof Cell) {
a.push(item(noun.head));
noun = noun.tail;
}
return a;
};
};

const tree = function (item: EnjsFunction): (n: Noun) => Json[] {
return function (noun) {
let a: Json[] = [];
if (noun instanceof Cell) {
if (!(noun.tail instanceof Cell)) {
throw new Error("tree: malformed");
}
a = [
...a,
item(noun.head),
...tree(item)(noun.tail.head),
...tree(item)(noun.tail.tail),
];
}
return a;
};
};

const cord = function (noun: Noun): string {
if (!(noun instanceof Atom)) {
throw new Error(`cord: noun not atom ${noun.toString()}`);
}
return Atom.cordToString(noun);
};

const tape = function (noun: Noun): string {
return (array(((n: Noun) => {
if (n.isCell()) {
throw new Error("tape: malformed");
}
return Atom.cordToString(n);
}))(noun)).join();
}

const numb = function (noun: Noun): number | string {
if (!(noun instanceof Atom)) {
throw new Error("numb: noun not atom");
}
if (bitLength(noun.number) <= 32) {
return Number(noun.number);
} else {
return noun.number.toString();
}
};

const numb32 = function (noun: Noun): number {
if (!(noun instanceof Atom)) {
throw new Error("numb32: noun not atom");
}
if (bitLength(noun.number) > 32) {
throw new Error("numb32: number too big");
}
return Number(noun.number);
}

const numbString = function (noun: Noun): string {
if (!(noun instanceof Atom)) {
throw new Error("numbString: noun not atom");
}
return noun.number.toString();
}

const loob = function (noun: Noun): boolean {
return noun.loob();
};

const nill = function (noun: Noun): null {
if (!(noun instanceof Atom && noun.number === 0n)) {
throw new Error("nill: not null");
}
return null;
};

const path = array(cord);

const enjs = {
frond,
tuple,
pairs,
pair,
array,
loob,
tree,
cord,
tape,
numb,
numb32,
numbString,
path,
buccen,
bucwut,
nill,
};

export { enjs };
Loading