Skip to content

Instantly share code, notes, and snippets.

@zerobias
Created November 13, 2018 04:25
Show Gist options
  • Save zerobias/639c5aa6e7461ec4f6a7405098e2ed4e to your computer and use it in GitHub Desktop.
Save zerobias/639c5aa6e7461ec4f6a7405098e2ed4e to your computer and use it in GitHub Desktop.
Don't try this at home
//@flow
import {Stack} from "./Stack";
import {ExecutionContext} from "./ExecutionContext";
import {Api} from "./Api";
import * as ASM from "./ASM";
import {walk} from "./walk";
const log = console.log.bind(console);
const zone = (name, fn) => api =>
api
.watch(data => log("[begin zone]", name, data))
.scope(name, fn)
.watch(data => log("[end zone]", name, data));
const createEvent = name => payload => api =>
api.add("runtime", payload).emit(name);
const setState = (name, payload) => api =>
api.add(name, payload).emit(name);
const alpha = createEvent("alpha");
const routine = thunk => {
const subroutine = thunk();
return api =>
api.thruRuntime((ctx, api) => {
ctx.callStack.push(ctx.currentCallStack);
ctx.currentCallStack = subroutine.cmd.reverse();
});
};
const thread1 = new Api()
.add("baz_tag", {baz: 100})
.watch((data, ctx, name) => {
log(`[sub ${name}]`, data(), ctx);
})
.tag("baz_tag", (val, ctx, stack, tag) =>
log(`[${tag}]`, val, ctx)
);
const subroutine = new Api()
.add("baz_tag", {baz: 100})
.watch((data, ctx, name) => log(`[subroutine ${name}]`, data, ctx))
.tag("baz_tag", (val, ctx, stack, tag) =>
log(`[${tag}]`, val, ctx)
);
const betaScope = (api, name) =>
api
// .stop(name)
.watch((data, ctx) => log(`[${name} scope]`, data, ctx))
.add("foo_key", {thru: "foo key"})
// .thruRuntime(ctx => ctx.currentCallStack.show())
.thruRuntime((ctx, api) => {
ctx.callStack.push(ctx.currentCallStack);
ctx.currentCallStack = subroutine.cmd.reverse();
})
.thruRuntime(ctx => ctx.currentCallStack.show())
.dynamic("foo_key")
.watch((data, ctx) => log(`[${name} scope]`, data, ctx))
.tag("bar_tag", (val, ctx, stack, tag) =>
log(`[${tag}]`, val, ctx)
);
const api = new Api()
//.stop()
.watch((data, ctx) => log("[begin]", data, ctx))
.mov("runtime", "L1")
.emit("alpha")
.thru(alpha("gamma+"))
.tag("bar_tag", () => {
//unreachable as bar_tag wasn't defined
throw "forbidden";
})
.add("bar_tag", {bar: 10})
.add("runtime", {baz: "from runtime"})
.thru(setState("state alpha", "bar value"))
.mov("L1", "state alpha")
.add("thread 1", thread1)
.add("L1", () => Math.random() > 0.5)
.add("left", 10)
.copy("right", "left")
.eq("left", "right", "lreq")
.tag("lreq", res => log("lreq", res))
.mapTag("left", n => n + 1)
.eq("left", "right", "lreq")
.tag("lreq", res => log("lreq", res))
// .scope("run", api => api.thread("thread 1"))
.thread("thread 1")
.scope("beta", betaScope)
.map(str => str.length)
.map(str => "str length: " + str)
// .scope("foo_scope", api =>
// api
// .watch(data => log("begin foo_scope", data))
// .map(str => "str length: " + str)
// )
// .unscope("alpha")
.watch((data, ctx) => log("[end]", data, ctx));
const walker = () => {
const api = new Api().copy("val", "runtime").dynamic("val");
//for (let i = 0; i < 10000; i++) {
// api.mapTag("val", n => n + 1);
// .add("isNum", new Api())
//walk(api, new Stack().push("aleph"));
//}
api.watch((val, ctx) => {
ctx.val += 1;
//ctx;
// console.log(val);
});
// console.log(api.cmd.array());
const runtime = new Stack().push({val: 1});
for (let i = 0; i < 100000; i++) {
walk(api, runtime);
}
};
// const timeStart = process.hrtime();
// walker() /*?.*/;
walk(api, new Stack().push("aleph"));
// const diff = process.hrtime(timeStart);
// console.log(`Benchmark took ${diff[0] * 1e9 + diff[1]} nanoseconds`);
//@flow
import {Stack} from "./Stack";
import {ExecutionContext} from "./ExecutionContext";
import * as ASM from "./ASM";
function push(cmd: $Values<typeof ASM>, args, api: Api): Api {
api.cmd.push([++api.index, cmd, args]);
return api;
}
export class Api {
cmd = new Stack();
parent: Stack;
index = 0;
emit(name: string) {
return push(ASM.EMIT, name, this);
}
map(fn: Function) {
return push(ASM.MAP, fn, this);
}
watch(fn: Function) {
return push(ASM.WATCH, fn, this);
}
scope(name: string, fn: (_: Api, name: string) => Api) {
let api = new Api();
api.parent = this.cmd;
api = fn(api, name);
return push(ASM.SCOPE, [name, api], this);
}
dynamic(from: string) {
return push(ASM.DYNAMIC, [from], this);
}
when(pred, then) {
return push(ASM.WHEN, [pred, then], this);
}
mapTag(tag: string, fn: Function) {
push(ASM.MOV, ["L1", tag], this);
push(ASM.MAP, fn, this);
return push(ASM.MOV, [tag, "L1"], this);
}
mov(to: string, from: string) {
return push(ASM.MOV, [to, from], this);
}
copy(to: string, from: string) {
return push(ASM.COPY, [to, from], this);
}
add(to: string, value: mixed) {
return push(
ASM.ADD,
[to, value, {toRuntime: to === "runtime", toL1: to === "L1"}],
this
);
}
tag(
tag: string,
fn: (
lastValue: mixed,
ctx: mixed,
tagStack: Stack,
tag: string
) => mixed
) {
return push(ASM.TAG, [tag, fn], this);
}
eq(
tagA: string,
tagB: string,
result: string,
comp: (a: any, b: any) => boolean = (a, b) => a === b
) {
return push(ASM.EQUAL, [tagA, tagB, result, comp], this);
}
thread(name: string) {
return push(ASM.THREAD, [name], this);
}
thru<T>(fn: (api: Api) => T): T {
return fn(this);
}
thruRuntime(fn: (context: ExecutionContext, api: Api) => mixed) {
return push(ASM.RUNTIME, fn, this);
}
stop(name: string = "root") {
return push(ASM.STOP, name, this);
}
use(fn: (o: *) => void) {
const o = {};
o.watch = this.watch.bind(this);
o.tag = this.tag.bind(this);
o.mov = this.mov.bind(this);
o.emit = this.emit.bind(this);
o.map = this.map.bind(this);
o.add = this.add.bind(this);
o.stop = this.stop.bind(this);
o.dynamic = this.dynamic.bind(this);
o.thruRuntime = this.thruRuntime.bind(this);
o.thru = this.thru.bind(this);
o.scope = (name, fn) =>
this.scope(name, (api, name) => api.use(fn));
o.watch = this.watch.bind(this);
fn(o);
return this;
}
result() {
const clone = this.cmd.clone();
return clone.values();
}
}
//@flow
export const EMIT: "emit" = "emit";
export const MAP: "map" = "map";
export const WATCH: "watch" = "watch";
export const SCOPE: "scope" = "scope";
export const STOP: "stop" = "stop";
export const MOV: "mov" = "mov";
export const COPY: "copy" = "copy";
export const THREAD: "thread" = "thread";
export const EQUAL: "equal" = "equal";
export const WHEN: "when" = "when";
export const TAG: "tag" = "tag";
export const ADD: "add" = "add";
export const RUNTIME: "runtime" = "runtime";
export const DYNAMIC: "dynamic" = "dynamic";
//@flow
export class Node<T> {
next: null | Node<T> = null;
prev: null | Node<T> = null;
data: T;
constructor(data: T) {
this.data = data;
}
}
/**
* A linked list implementation in JavaScript.
*/
export class DoubleLinkedList<T> {
/**
* Pointer to first item in the list.
* @property head
* @type Object
* @private
*/
head: Node<T> | null = null;
/**
* Pointer to last item in the list.
* @property tail
* @type Object
* @private
*/
tail: Node<T> | null = null;
length: number = 0;
/**
* Appends some data to the end of the list. This method traverses
* the existing list and places the value at the end in a new item.
* @param {variant} data The data to add to the list.
* @return {Void}
* @method add
*/
add(data: T) {
//create a new item object, place data in
const node = new Node(data);
//special case: no items in the list yet
if (this.length == 0) {
this.head = node;
this.tail = node;
} else {
const {tail} = this;
if (tail) {
//attach to the tail node
tail.next = node;
node.prev = tail;
this.tail = node;
}
}
//don't forget to update the count
this.length++;
}
/**
* Retrieves the data in the given position in the list.
* @param {int} index The zero-based index of the item whose value
* should be returned.
* @return {variant} The value in the "data" portion of the given item
* or null if the item doesn't exist.
* @method item
*/
item(index: number): T | null {
//check for out-of-bounds values
if (index > -1 && index < this.length) {
let current = this.head;
let i = 0;
while (i++ < index) {
if (current !== null) current = current.next;
}
return current ? current.data : null;
} else {
return null;
}
}
/**
* Removes the item from the given location in the list.
* @param {int} index The zero-based index of the item to remove.
* @return {variant} The data in the given position in the list or null if
* the item doesn't exist.
* @method remove
*/
remove(index: number): T | null {
//check for out-of-bounds values
if (index > -1 && index < this.length) {
let current = this.head;
let i = 0;
//special case: removing first item
if (index === 0) {
if (current) {
this.head = current.next;
}
/*
* If there's only one item in the list and you remove it,
* then this.head will be null. In that case, you should
* also set this.tail to be null to effectively destroy
* the list. Otherwise, set the previous pointer on the new
* this.head to be null.
*/
if (!this.head) {
this.tail = null;
} else {
this.head.prev = null;
}
//special case: removing last item
} else if (index === this.length - 1) {
current = this.tail;
if (current) {
const tail = (this.tail = current.prev);
if (tail) tail.next = null;
}
} else {
//find the right location
while (i++ < index) {
if (current) current = current.next;
}
//skip over the item to remove
if (current) {
if (current.prev !== null) {
current.prev.next = current.next;
if (current.next !== null) {
current.next.prev = current.prev;
}
}
}
}
//decrement the length
this.length--;
//return the value
return current ? current.data : null;
} else {
return null;
}
}
/**
* Returns the number of items in the list.
* @return {int} The number of items in the list.
* @method size
*/
size() {
return this.length;
}
/**
* Converts the list into an array.
* @return {Array} An array containing all of the data in the list.
* @method toArray
*/
toArray(reverse: boolean = false) {
const result = [];
let current = reverse ? this.tail : this.head;
while (current) {
result.push(current.data);
current = reverse ? current.prev : current.next;
}
return result;
}
*iterator(): Iterator<T> {
let current = this.head;
while (current) {
yield current.data;
current = current.next;
}
}
/**
* Converts the list into a string representation.
* @return {String} A string representation of the list.
* @method toString
*/
toString() {
return this.toArray().toString();
}
}
//@flow
import {Stack} from "./Stack";
import {Register} from "./plexor";
class Interruption {}
class Interruptor {}
let id = 0;
export class Device {}
export class ExecutionContext {
id = ++id;
L1 = new Stack();
register = new Register();
nameStack = new Stack().push("root");
runtime: Stack;
callStack = new Stack();
currentCallStack: Stack;
dataStack = new Stack().push({name: "root"});
taggedMap: Map<string, Stack> = new Map();
clear() {
this.taggedMap.clear();
}
}
//@flow
import {Stack} from "./Stack";
export const __innerStack = (s: Stack, serialize: Function) => {
const getSpace = () =>
Array.from(new Array(__printLevel * 2), () => " ").join("");
const result = [getSpace(), s.name ? s.name : "stack"];
__printLevel += 1;
s.forEach((v, n) => {
result.push(`\n`, getSpace(), ...serialize(v));
});
__printLevel -= 1;
return result;
};
let __printLevel = 0;
export const printStack = (
s: Stack,
serialize = x => (x instanceof Stack ? printStack(x) : [x])
) => {
const getSpace = () =>
Array.from(new Array(__printLevel * 2), () => " ").join("");
const result = [`\n`, getSpace(), s.name ? s.name : "stack"];
__printLevel += 1;
s.forEach((v, n) => {
result.push(`\n`, getSpace(), ...serialize(v));
});
__printLevel -= 1;
return result;
};
export const log = console.log.bind(console);
const byLine = (arr, spaces, space) => {
if (arr.length < 2) return arr;
return arr.reduce((acc, v) => spaces(acc, v, space), []);
};
const printScope = (acc, space, [id, , [tag, {cmd}]]) => {
let accum;
if (acc.length === 0 || acc[0] === "[]") accum = [space.slice(1)];
else accum = [...acc, space];
return [
...accum,
...[
[id, tag],
...cmd
.array()
.reduceRight(
(acc, v) => [...acc, space + " ", [v[0], v[1]]],
[]
),
],
];
};
export const logNode = (
[past, now, next],
map = x => x,
spaces = (acc, x, space) => {
const add = [];
if (acc.length !== 0) add.push(space);
if (x[1] === "scope") return printScope(acc, space, x);
return [...acc, ...add, [x[0], x[1]]];
// acc.length === 0 ? [x] : ["\n ", x])
},
space = "\n "
) => {
let pastRes = ["[]"];
let nextRes = ["[]"];
if (past.length > 0) pastRes = past;
if (next.length > 0) nextRes = next;
let lres = byLine(pastRes, spaces, space);
if (lres[0] === "[]") lres = [];
let rres = byLine(nextRes, spaces, space);
if (rres[0] === "[]") rres = [];
let mapNow = map(now || []);
if (mapNow[1] === "scope")
mapNow = [[mapNow[0]], "scope " + now[2][0] + " [...]"];
else mapNow = [mapNow];
log(" ", ...lres);
log("now ", ...mapNow);
log(" ", ...rres, `\n`);
};
//@flow
import {__innerStack} from "./printStack";
export type Stacked = [mixed, [] | Stacked];
export class Stack {
_: Stacked = ["unit", []];
size = 0;
/*::
name: string;
*/
push(value: any): Stack {
this._ = [value, this._];
this.size += 1;
return this;
}
pop() {
if (this.size === 0) return "unit";
const [value, stack] = this._;
this._.length = 0;
this._ = stack;
this.size -= 1;
return value;
}
clone(): Stack {
const result = new Stack();
result.size = this.size;
let next = [];
let target: Stacked = [this._[0], next];
let reference = this._[1];
result._ = target;
while (reference.length) {
next[0] = reference[0];
target = next;
target[1] = next = [];
reference = reference[1];
}
// if (this.name) result.name = this.name;
return result;
}
reverse() {
const result = new Stack();
for (const value of this.values()) {
result.push(value);
}
// if (this.name) result.name = this.name;
return result;
}
slice(index: number) {
if (this.size <= index) return;
let stack = this._[1];
let prev = this._;
let size = this.size;
while (size > index) {
prev.length = 0;
prev = stack;
stack = stack[1];
size -= 1;
}
this.size = size;
this._ = stack;
}
peek() {
return this._[0];
}
forEach(
fn: (val: mixed, n: number) => mixed,
thisArg?: any = null
) {
for (const [n, v] of this.entries()) fn(v, n);
}
*entries(): Iterator<[number, mixed]> {
if (this.size === 0) return;
let [value, stack] = this._;
let size = this.size;
yield [size, value];
while ((size -= 1)) {
value = stack[0];
stack = stack[1];
yield [size, value];
}
}
array() {
if (this.size === 0) return [];
const result = [];
let [value, stack] = this._;
let size = this.size;
// yield value;
result.push(value);
while ((size -= 1)) {
value = stack[0];
stack = stack[1];
// yield value;
result.push(value);
}
return result; // [...this.values()];
}
show(print?: boolean) {
if (print === undefined) print = true;
const result = __innerStack(
this,
x => (x instanceof Stack ? x.show(false) : [x])
);
if (print) console.log(...result);
else return result;
}
*values(): Iterator<mixed> {
if (this.size === 0) return;
let [value, stack] = this._;
let size = this.size;
yield value;
while ((size -= 1)) {
value = stack[0];
stack = stack[1];
yield value;
}
}
}
export class StackIterator {
target: Stack;
_: Stacked;
source: Stacked;
step = 0;
done: boolean;
next() {
if (this.done) return;
if (this._.length !== 2) {
this.done = true;
return;
}
const [value, _] = this._;
if (value === "unit") {
this.done = true;
return;
}
if (_.length !== 2) {
this.done = true;
return;
}
this._ = _;
this.step += 1;
return value;
}
array(map: Function = x => x) {
const result = [];
let value;
while ((value = this.next()) !== undefined)
result.push(map(value));
return result;
}
node() {
const rest = this.clone()
.array()
.slice(1);
const past = this.clone()
.sync()
.array();
let current = past[this.step];
return [past.slice(0, this.step), current, rest];
}
reset() {
this.step = 0;
this._ = this.source;
this.done = this.target.size === 0;
return this;
}
sync() {
this._ = this.target._;
return this;
}
clone() {
const clone = new StackIterator();
clone.target = this.target;
clone.step = this.step;
clone._ = this._;
clone.source = this.source;
return clone;
}
static iterate(target: Stack): StackIterator {
const result = new StackIterator();
result.target = target;
result.source = result.target._;
result.reset();
// result.next();
return result;
}
static iterateBack(target: Stack): StackIterator {
const result = new StackIterator();
result.target = target.reverse();
result.source = result.target._;
return result.reset();
}
}
//@flow
import {Stack, StackIterator} from "./Stack";
import {log, logNode} from "./printStack";
import {ExecutionContext} from "./ExecutionContext";
import {Api} from "./Api";
import * as ASM from "./ASM";
export const walk = (api: Api, runtime: Stack = new Stack()) => {
// api.cmd.name = "api.cmd";
const ctx = new ExecutionContext();
ctx.runtime = runtime;
const {callStack, L1, dataStack} = ctx;
ctx.callStack.push(api.cmd.reverse());
// ctx.dataStack.name = "dataStack";
// ctx.callStack.name = "callStack";
// ctx.nameStack.name = "nameStack";
// ctx.L1.name = "L1";
let item;
let dataCurrent;
walk: while (
(ctx.currentCallStack = ctx.callStack.pop()) !== "unit"
) {
// ctx.currentCallStack.name = "currentCallStack";
// const iter = StackIterator.iterate(ctx.currentCallStack.clone());
// logNode(iter.node(), ([id, name]) => [id, name]);
dataCurrent = ctx.dataStack.peek();
current: while (ctx.currentCallStack.size) {
// ctx.nameStack.show();
// iter.next();
// logNode(iter.node(), ([id, name]) => [id, name]);
// log(...printStack(ctx.currentCallStack));
item = ctx.currentCallStack.pop();
const [index, type, value] = item;
// log(index, type, value, dataCurrent);
step: switch (type) {
case ASM.EMIT: {
// const data = L1.peek();
break step;
}
case ASM.RUNTIME:
value(
ctx,
api,
ctx.L1.peek(),
dataCurrent,
ctx.nameStack.peek()
);
break step;
case ASM.MOV: {
const [to, from] = value;
if (fastcall(to, from, ctx)) break step;
const fromValue = fromName(from, ctx);
if (!fromValue) break step;
let toValue = fromName(to, ctx);
if (!toValue) {
toValue = new Stack();
ctx.taggedMap.set(to, toValue);
}
toValue.push(fromValue.pop());
break step;
}
case ASM.COPY: {
const [to, from] = value;
if (fastcall(to, from, ctx)) break step;
const fromValue = fromName(from, ctx);
if (!fromValue) break step;
let toValue = fromName(to, ctx);
if (!toValue) {
toValue = new Stack();
ctx.taggedMap.set(to, toValue);
}
toValue.push(fromValue.peek());
break step;
}
case ASM.DYNAMIC: {
const [from] = value;
const fromValue = fromName(from, ctx);
if (!fromValue) break step;
ctx.callStack.push(ctx.currentCallStack);
ctx.dataStack.push(fromValue.pop());
continue walk;
}
case ASM.WHEN: {
const [pred, then] = value;
const threadStack: Stack | void = fromName(then, ctx);
const predStack: Stack | void = fromName(pred, ctx);
if (threadStack === undefined) break step;
if (predStack === undefined) break step;
if (!predStack.peek()) break step;
const thread: Api | void = threadStack.peek();
if (thread === undefined) break step;
const thenVal = then.peek();
ctx.nameStack.push(name);
ctx.dataStack.push({name});
ctx.callStack.push(ctx.currentCallStack);
ctx.callStack.push(then.cmd.reverse());
break step;
}
case ASM.EQUAL: {
const [aTag, bTag, resultTag, comparator] = value;
const forA = fromName(aTag, ctx);
if (forA === undefined) break step;
const forB = fromName(bTag, ctx);
if (forB === undefined) break step;
let res = fromName(resultTag, ctx);
if (!res) {
res = new Stack();
ctx.taggedMap.set(resultTag, res);
}
const computed = comparator(forA.peek(), forB.peek());
res.push(computed);
ctx.dataStack.push(computed);
break step;
}
case ASM.ADD: {
const [to, val, {toRuntime, toL1}] = value;
if (toRuntime) {
runtime.push(val);
} else if (toL1) {
ctx.L1.push(val);
} else {
let tagged = ctx.taggedMap.get(to);
if (!tagged) {
tagged = new Stack();
ctx.taggedMap.set(to, tagged);
}
tagged.push(val);
}
break step;
}
case ASM.TAG: {
const [tag, fn] = value;
const tagged = ctx.taggedMap.get(tag);
if (tagged) {
const current = tagged.peek();
fn(current, dataCurrent, tagged, tag);
}
break step;
}
case ASM.MAP:
ctx.L1.push(value(ctx.L1.pop(), dataCurrent));
break step;
case ASM.WATCH:
value(ctx.L1.peek(), dataCurrent, ctx.nameStack.peek());
break step;
case ASM.THREAD: {
const [name] = value;
const threadStack: Stack | void = ctx.taggedMap.get(name);
if (!threadStack) break step;
const thread: Api | void = threadStack.peek();
if (!thread) break step;
ctx.nameStack.push(name);
ctx.dataStack.push({name});
ctx.callStack.push(ctx.currentCallStack);
ctx.callStack.push(thread.cmd.reverse());
continue walk;
}
case ASM.SCOPE: {
const [name, api] = value;
ctx.nameStack.push(name);
ctx.dataStack.push({name});
ctx.callStack.push(ctx.currentCallStack);
ctx.callStack.push(api.cmd.reverse());
continue walk;
}
// case ASM.STOP:
// break current;
case ASM.STOP: {
if (ctx.currentCallStack.peek() === value) break current;
let back = -1;
const found = ctx.nameStack
.array()
.findIndex((x, i) => value === x);
if (found === 0) break current;
if (found > 0) {
for (let i = 0; i < found; i++) {
ctx.callStack.pop();
}
break current;
}
log("CAN NOT STOP", value);
break step;
}
}
}
ctx.dataStack.pop();
ctx.nameStack.pop();
}
ctx.clear();
};
const fastcall = (to, from, ctx) => {
switch (to) {
case "ax":
case "bx":
case "cx":
switch (from) {
case "ax":
case "bx":
case "cx":
ctx.register[to] = ctx.register[from];
return true;
}
}
return false;
};
const fromName = (name, ctx) => {
switch (name) {
case "runtime":
return ctx.runtime;
case "L1":
return ctx.L1;
default:
return ctx.taggedMap.get(name);
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment