Select Git revision
hunks.js 9.50 KiB
/* --------------------------- ---------------------------- */
/* ------------------------ HUNKITUP ------------------------- */
/* --------------------------- ---------------------------- */
import {
TSET,
findPhy
} from '../typeset.js'
function Hunkify(hunk) {
// scripting languages should name hunks by their script location and filename
// compiled languages will assign a string
// because this is always added by .addHunk(path) (path == name)
// we set this during load, only once, this avoids some confusion
hunk.type = null
hunk.name = null
// we keep a copy of our position-in-the-array ... of our parent ... our pip
hunk.ind = null
hunk.log = function(msg) {
let str = `LG from ${hunk.name} at ${hunk.ind}: `
for (let i = 0; i < arguments.length; i++) {
str += arguments[i]
if (i < arguments.length - 1) str += ', '
}
console.log(str)
}
// input, output, and state: ay balls, here we go
hunk.inputs = new Array()
hunk.outputs = new Array()
hunk.states = new Array()
hunk.input = (type, name) => {
let ip = new Input(type, name, hunk)
hunk.inputs.push(ip)
return ip
}
hunk.output = (type, name) => {
let op = new Output(type, name, hunk)
hunk.outputs.push(op)
return op
}
hunk.state = (type, name, dfault) => {
let st = new State(type, name, dfault)
hunk.states.push(st)
return st
}
// top-secret backdoor
hunk.mgr = {}
}
/* --------------------------- ---------------------------- */
/* ------------------------- OUTPUT -------------------------- */
/* --------------------------- ---------------------------- */
function Output(type, name, parent, linktype) {
// naming, etc
this.name = name
this.type = type
this.phy = findPhy(this.type) // checks, throws error if type doesn't exist
this.parent = parent
// store,
this.ref = null
this.data = null
// hookup
this.connections = new Array()
// check in realtime, manager does less
this.io = () => {
for (var i = 0; i < this.connections.length; i++) {
// connection arrays contain 'wires' ... these are stateful
if (this.connections[i].io) return true
}
return false
}
// when we put, we use this to set states
this.setIo = () => {
for (var i = 0; i < this.connections.length; i++) {
this.connections[i].io = true
}
}
/* --------------------------- ---------------------------- */
/* ------------ OUTPUT PUT AND TRANSPORT (TYPED) ------------- */
/* --------------------------- ---------------------------- */
// yonder put call, transport call
// copy-in (chances are that after .put call the referenced object changes, before it is copied out)
// and copy-out (same on the other side, we are potentially passing to many downstream, need a different copy for each)
// typing ... odd ?
this.put = (data) => {
if (!this.io()) {
this.ref = data
this.data = this.phy.copy[this.type](data)
this.setIo()
return true
} else {
console.error(`WARNING: put called on occupied output: output ${this.name} in ${this.parent.name}`)
return false
}
}
/* --------------------------- ---------------------------- */
/* ---------------- OUTPUT MANAGE CONNECTIONS ---------------- */
/* --------------------------- ---------------------------- */
this.attach = (input) => {
// we can only do this if a fn exists for us to copy from-us-to-them
if (this.phy.copy[input.type]) {
// the wire is the stateful part,
let wire = {
op: this,
ip: input,
io: false
}
input.connections.push(wire)
this.connections.push(wire)
return true
} else {
console.error(`ERROR: missing type conversion from: ${this.type} to ${input.type}`)
return false
}
}
this.remove = (input) => {
// find by names pls
let ind = this.connections.findIndex((cand) => {
return (cand.ip === input)
})
if (ind !== -1) {
input.disconnect(this)
this.connections.splice(ind, 1)
return true
} else {
console.log('ERROR: wire removal, was looking for', input, 'in', this.connections)
return false
}
}
this.findOwnIndex = () => {
// find self in parent's inputs
let index = this.parent.outputs.findIndex((cand) => {
return (cand.name === this.name && cand.type === this.type)
})
if (index === -1) {
console.log(`output could not find itself in parent: ${index}`, this, 'unstrung', this.parent.outputs, 'strung', JSON.parse(JSON.stringify(this.parent.outputs)))
throw new Error(`output could not find itself in parent: ${index}`)
}
return index
}
this.disconnectAll = () => {
for (let cn of this.connections) {
// here we're disconnecting the input, meaning the input is removing this from its list of connections
cn.ip.disconnect(this)
}
// this is actually a genuine way to delete an array in js, http://shrugguy.com
this.connections.length = 0
}
/* --------------------------- ---------------------------- */
/* ----------- OUTPUT TRICKS FOR LINK CONNECTIONS ------------ */
/* --------------------------- ---------------------------- */
if (linktype) {
// has stash ?
this.needsAck = false;
}
} // FIN output
/* --------------------------- ---------------------------- */
/* -------------------------- INPUT -------------------------- */
/* --------------------------- ---------------------------- */
function Input(type, name, parent, linktype) {
// naming, etc
this.name = name
this.type = type
this.parent = parent
this.phy = findPhy(this.type)
// we keep access to the outputs we're hooked up to here
this.connections = new Array()
this.io = () => {
// if any of our connections have data that we haven't read once yet,
for (var i = 0; i < this.connections.length; i++) {
// have to update calls to .io -> .io()
if (this.connections[i].io) return true
}
return false
}
// get calls are now a bit messier,
// we have multiple wires,
this.lastGet = 0 // place we read from last (have multiple wires case)
this.get = () => {
// we want to (roughly) round robin these, so we pick up from where we last pulled,
var i = this.lastGet
// find the next occupied conn: do iterations max. of total length of connections,
for (var u = 0; u < this.connections.length; u++) {
// increment, wrap
i++
if (i >= this.connections.length) i = 0
// do work,
if (this.connections[i].io) {
let wire = this.connections[i]
// and set that wire to read / empty
wire.io = false
// and we'd like to remember from whence we last took data
this.lastGet = i
// get this data out ... we have to copy it out again, otherwise
// downstream operators may write to objects that have handles elsewhere
// here we call the function from typeset.js that is custom between the-output -> and the-input
return wire.op.phy.copy[this.type](wire.op.data)
}
}
// if we pass this for w/o hitting return,
console.error(`WARNING: get called on occupied input: ${this.name} in ${this.parent.name}`)
return null
}
this.findOwnIndex = () => {
// find self in parent's inputs
let index = this.parent.inputs.findIndex((cand) => {
return (cand.name === this.name && cand.type === this.type)
})
if (index === -1) throw new Error('input could not find itself in parent')
return index
}
this.disconnect = (output) => {
let index = this.connections.findIndex((cand) => {
return (cand.op = output)
})
if (index === -1) throw new Error('during output disconnect, input cannot find output...')
this.connections.splice(index, 1)
// find the reference in our array,
}
this.disconnectAll = () => {
for (let cn of this.connections) {
cn.op.remove(this)
}
}
// inputs / outputs for 'link' (a hunk) ports have more state,
if (linktype) {
// assume clear upstream on startup, maybe dangerous?
this.icus = true
}
}
// working the onChange / change function (onChange feels more better?)
// if we do .on('change', (val) => {
//})
// we can potentially add ... .on('startup') or something, to set hard defaults ?
// and then: write out copy fn's per item ...
function State(type, name, startup) {
this.name = name
this.type = type
this.phy = findPhy(this.type) // types must have *some* code -> by finding one here, we check that it exists
// TODO pls add check for missing startup value ?
this.value = startup
// this is still something of a hack
// during load, the manager gets in here and dishes a function 2 us
this.hookup = (newval) => {
console.error('ERROR: when state-change request made, no hookup function yet - probable manager error while loading hunk')
}
// from within a hunk, we typically call this
// to update the value and ship it out to any views
this.set = (value) => {
// for good measure, we copy-in state as well. who knows.
this.value = this.phy.copy[this.type](value)
this.hookup(this.value, this.msgid)
this.msgid = null
}
this.msgid = null
// the manager calls this fn, when a request is made.
//
this.tryChange = (value, msgid) => {
// coupla steps here, we track and then call internal fn,
this.msgid = msgid
// not sure, but we could clean these here by doing this.phy.copy[this.type](value)
if (this.onChange) {
this.onChange(value)
} else {
this.set(value)
}
}
}
export {
Hunkify,
Input,
Output,
State
}