@lf-lang/reactor-ts
Version:
A reactor-oriented programming framework in TypeScript
195 lines • 7.18 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Component = void 0;
const internal_1 = require("./internal");
/**
* Base class for named objects embedded in a hierarchy of reactors. Each
* component can only be owned by a single reactor instance. All members of
* this class are prefixed with an underscore to avoid name collisions with
* ports, actions, timers, or reactor instances that may be part of the
* interface of a `Reactor`, which extends this class.
*
* @author Marten Lohstroh (marten@berkeley.edu)
*/
class Component {
static pathSeparator = ".";
/**
* A symbol that identifies this component, and it also used to selectively
* grant access to its privileged functions.
*/
_key = Symbol("Unique component identifier");
/**
* The container of this component. Each component is contained by a
* reactor. Only instances of `App`, which denote top-level reactors,
* are self-contained.
*/
_container;
/**
* Create a new component and register it with the given container.
* @param container The reactor that will contain the new component,
* `null` if this is an instance of `App`, in which case the component
* will be designated as its own container.
*
* Note that each subclass implementation needs to call the method
* `_linkToRuntimeObject` immediately after calling this super
* constructor in order to establish a link with the runtime object.
* @param alias An optional alias for the component.
*/
constructor(container) {
if (container !== null) {
// Register.
container._register(this, this._key);
// And set the container.
this._container = container;
}
else {
if (this instanceof internal_1.App) {
// Apps are self-contained.
this._container = this;
}
else {
throw new Error("Cannot instantiate component without a parent.");
}
}
}
/**
* Request the container to pass down its runtime object to this component.
* This function is to be called once and only once upon the construction
* of an object that subclasses `Component`. If it is called more than once
* a runtime error results.
*/
_linkToRuntimeObject() {
this._getContainer()._requestRuntimeObject(this);
}
/**
* Report whether this component has been registered with its container or not.
* In principle, all components should have a container, but at the time of
* their construction there is a brief period where they are not. This is the
* only moment that a component is allowed register with its container.
*/
_isRegistered() {
return this._getContainer() !== undefined;
}
/**
* Confirm whether or not this component is contained by the given reactor.
* @param reactor The presumptive container of this component.
*/
_isContainedBy(reactor) {
if (this instanceof internal_1.App)
return false;
else if (this._container === reactor)
return true;
return false;
}
/**
* Confirm whether or not this component is contained by the container of
* the given reactor.
* @param reactor The container presumptive container of the container of
* this component.
*/
_isContainedByContainerOf(reactor) {
if (this instanceof internal_1.App)
return false;
else if (this._container._isContainedBy(reactor))
return true;
return false;
}
/**
* Return a string that identifies this component.
* The name is a path constructed as `[App]/[..]/[Container]/[This]`.
*/
_getFullyQualifiedName() {
if (!(this instanceof internal_1.App)) {
return (this._container._getFullyQualifiedName() +
Component.pathSeparator +
this._getName());
}
else {
return this._getName();
}
}
/**
* Given a component and its container (the global object if the component
* is an `App`), return the key of the entry that matches the component.
* @param component a component of which the object is assumed to be its
* container
* @param object the assumed container of the component
* @returns the key of the entry that matches the component
*/
static keyOfMatchingEntry(component, object) {
for (const [key, value] of Object.entries(object)) {
if (value === component) {
return `${key}`;
}
}
return "";
}
/**
* Given a port and its containing reactor, return the key of the entry that matches
* a multiport found in the reactor that matches the port.
* @param port a port that is assumed to be a constituent of a multiport declared on
* the given reactor
* @param reactor a reactor that is assumed to have a multiport of which one of the
* constituents is the given port
* @returns an identifier for the port based on its location in a matching multiport
*/
static keyOfMatchingMultiport(port, reactor) {
for (const [key, value] of Object.entries(reactor)) {
if (value instanceof internal_1.MultiPort) {
const channels = value.channels();
for (let i = 0; i < channels.length; i++) {
if (channels[i] === port) {
return `${key}[${i}]`;
}
}
}
}
return "";
}
static keyOfMatchingBank(member, reactor) {
for (const [key, value] of Object.entries(reactor)) {
if (value instanceof internal_1.Bank) {
const members = value.all();
for (let i = 0; i < members.length; i++) {
if (members[i] === member) {
return `${key}[${i}]`;
}
}
}
}
return "";
}
/**
* Return a string that identifies this component within its container.
* If no such string was found, return the name of the constructor.
*/
_getName() {
let name;
if (this instanceof internal_1.App) {
name = this._name;
}
else {
name = Component.keyOfMatchingEntry(this, this._container);
}
if (name === "" && this instanceof internal_1.IOPort) {
name = Component.keyOfMatchingMultiport(this, this._container);
}
if (name === "" && this instanceof internal_1.Reactor) {
name = Component.keyOfMatchingBank(this, this._container);
}
if (name !== "") {
return name;
}
else {
return this.constructor.name;
}
}
/**
* Return the container of this component.
*/
_getContainer() {
return this._container;
}
}
exports.Component = Component;
//# sourceMappingURL=component.js.map