Skip to content
Snippets Groups Projects
Select Git revision
  • 66ed7fb0a5d7c1b62dc8d306d637b09b2085ff8e
  • master default protected
  • leo
  • dex
  • pendulum
  • apfelstruder
  • littlerascal
7 results

vfpc.js

Blame
  • 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
    }