avocado-old/packages/behavior/context/index.js
2019-03-17 23:45:48 -05:00

100 lines
2.3 KiB
JavaScript

import {Globals} from './globals';
import {TypeMap} from './types';
class Context extends Map {
add(key, value) {
this.set(key, value);
}
renderStepsUntilNow(steps, step) {
const stepsUntilNow = steps.slice(0, steps.indexOf(step));
return TypeMap.renderSteps(stepsUntilNow);
}
traverse(steps) {
return this.traverseAndDo(steps, (node, step) => {
return this.traverseOneStep(steps, node, step);
});
}
traverseAndDo(steps, fn) {
const [first, ...rest] = steps;
if ('key' !== first.type) {
throw new TypeError(`First step in a traversal must be type "key"`);
}
return rest.reduce((walk, step, index) => {
if (walk instanceof Promise) {
return walk.then((walk) => {
fn(walk, step, index);
});
}
else {
return fn(walk, step, index);
}
}, this.get(first.key));
}
traverseAndSet(steps, value) {
return this.traverseAndDo(steps, (node, step, index) => {
const isLastStep = index === steps.length - 2;
if (!isLastStep) {
return this.traverseOneStep(steps, node, step);
}
switch (step.type) {
case 'key':
return node[step.key] = value.get(this);
case 'invoke':
const rendered = this.renderStepsUntilNow(steps, step);
throw new ReferenceError(`invalid assignment to function "${rendered}"`);
}
});
}
traverseOneStep(steps, node, step) {
if ('undefined' === typeof node) {
const rendered = this.renderStepsUntilNow(steps, step);
throw TypeError(`"${rendered}" is traversed through but undefined`);
}
switch (step.type) {
case 'key':
return node[step.key];
case 'invoke':
return node(...step.args.map((arg) => arg.get(this)));
}
}
}
class TypedContext extends Context {
constructor(iterator) {
super(iterator);
this.types = {};
}
add(key, value, type) {
super.add(key, value);
if (!type) {
return;
}
this.types = {
...this.types,
[key]: type,
};
}
}
export function createContext() {
const context = new Context();
context.add('global', new Globals());
return context;
}
export function createTypedContext() {
const context = new TypedContext();
context.add('global', new Globals(), 'globals');
return context;
}