Package: src/packages/chips.fdoc

key file
chips.flx share/lib/std/control/chips.flx

Chips.

Standard components

Write block.

Blocks reader.

//[chips.flx]
open class BaseChips
{

chip writeblock[T]
  connector io
    pin inp : %<T
{
}

Read block.

Blocks writer.

//[chips.flx]
chip readblock[T]
  connector io
    pin inp: %>T
{
}

Universal sink

Reads input forever.

//[chips.flx]
chip sink[T]
  connector io
    pin inp : %<T
{
  while true do
    var x = read (io.inp);
    C_hack::ignore (x);
  done
}

Constant Source.

Write fixed value forever.

//[chips.flx]
chip source[T] (a:T)
  connector io
    pin out: %>T
{
  while true do
    write (io.out, a);
  done
}

One shot source

//[chips.flx]
chip value[T] (a:T)
  connector io
    pin out: %>T
{
  write (io.out, a);
}

Source from generator

//[chips.flx]
chip generator[T] (g: 1->T)
  connector io
    pin out: %>T
  {
    repeat perform write (io.out, g());
  }

Source from iterator

//[chips.flx]
chip iterate[T] (g: 1->opt[T])
  connector io
    pin out: %>T
  {
    again:>
      var x = g();
      match x with
      | Some v =>
        write (io.out, v);
        goto again;
      | None => ;
      endmatch;
  }

Source from list

//[chips.flx]
chip source_from_list[T] (a:list[T])
  connector io
    pin out: %>T
{
  for y in a perform write (io.out,y);
}

chip bound_source_from_list[T] (a:list[T])
  connector io
    pin out: %>opt[T]
{
  for y in a perform write (io.out,Some y);
  while true perform write (io.out,None[T]);
}

Function adaptor.

Converts function to chip.

//[chips.flx]
chip function[D,C] (f:D->C)
  connector io
    pin inp: %<D
    pin out: %>C
{
  while true do
    var x = read io.inp;
    var y = f x;
    write (io.out, y);
  done
}

Procedure adaptor.

Converts a procedure to a sink.

//[chips.flx]
chip procedure[D] (p:D->0)
  connector io
    pin inp: %<D
{
  while true do
    var x = read io.inp;
    p x;
  done
}

Filter

Convert a predicate and function to a transducer.

//[chips.flx]
chip filter[D,C] (c:D->bool) (f:D->C)
  connector io
    pin inp: %<D
    pin out: %>C
{
  while true do
    var x = read io.inp;
    if c x do
       write (io.out, f x);
    done
  done
}

chip filter[D,C] (f:D->opt[C])
  connector io
    pin inp: %<D
    pin out: %>C
{
  while true do
    var x = read io.inp;
    match f x with
    | Some y => write (io.out, y);
    | None => ;
    endmatch;
  done
}

Sink to list

//[chips.flx]
chip sink_to_list[T] (p: &list[T])
  connector io
    pin inp : %<T
{
  while true do
    var x = read (io.inp);
    p <- Cons (x,*p);
  done
}

Sink to unique list

//[chips.flx]
chip sink_to_unique_list[T with Eq[T]] (p: &list[T])
  connector io
    pin inp : %<T
{
  while true do
    var x = read (io.inp);
    if not (x in *p) perform
      p <- Cons (x,*p)
    ;
  done
}

Buffer.

One step buffer. Same as a function adaptor passed identity.

//[chips.flx]
chip buffer [T]
  connector io
    pin inp: %<T
    pin out: %>T
{
  while true do
    var x = read io.inp;
    write (io.out, x);
  done
}

chip dup [T]
  connector io
    pin inp: %<T
    pin out1: %>T
    pin out2: %>T
{
  while true do
    var x = read io.inp;
    write (io.out1, x);
    write (io.out2, x);
  done
}

Connector symbol

The syntax |-> is parsed to pipe (a,b). We add overloads for chips with pins named io.inp, io.out.

//[chips.flx]
// two transducers
chip pipe[T,U,V] (a:iochip_t[T,U],b:iochip_t[U,V])
 connector io
   pin inp: %<T
   pin out: %>V
{
  circuit
    connect a.out,b.inp
    wire io.inp to a.inp
    wire io.out to b.out
  endcircuit
}

// source to transducer
chip pipe[T,U] (a:ochip_t[T],b:iochip_t[T,U])
 connector io
   pin out: %>U
{
  circuit
    connect a.out,b.inp
    wire io.out to b.out
  endcircuit
}

// transducer to sink
chip pipe[T,U] (a:iochip_t[T,U],b:ichip_t[U])
 connector io
   pin inp: %<T
{
  circuit
    connect a.out,b.inp
    wire io.inp to a.inp
  endcircuit
}

// source to sink
proc pipe[T] (a:ochip_t[T],b:ichip_t[T])  ()
{
  circuit
    connect a.out,b.inp
  endcircuit
}

Debug Buffer.

//[chips.flx]
chip debug_buffer [T with Str[T]] (tag:string)
  connector io
    pin inp: %<T
    pin out: %>T
{
  while true do
    println$ "Debug buffer [" + tag + "] READ";
    var x = read io.inp;
    println$ "Debug buffer [" + tag + "] read " + x.str;
    write (io.out, x);
    println$ "Debug buffer [" + tag + "] written " + x.str;
  done
}

One Shot.

A one shot buffer.

//[chips.flx]
chip oneshot [T]
  connector io
    pin inp: %<T
    pin out: %>T
{
  var x = read io.inp;
  write (io.out, x);
}

Store

Stores read values in a variable.

//[chips.flx]
chip store[T] (p:&T)
  connector io
    pin inp: %<T
{
  while true do
    var x = read io.inp;
    p <- x;
  done
}

Fetch

Writes current value of a variable.

//[chips.flx]
chip fetch[T] (p:&T)
  connector io
    pin out: %>T
{
  while true do
    write (io.out, *p);
  done
}

Printer

Writes input to console.

//[chips.flx]
chip debug_sink [T with Str[T]] (s:string)
  connector io
    pin inp: %<T
{
  while true do
    var x = read io.inp;
    println$ "Debug sink ["+s+"] "+x.str;
  done
}

Asynchronous Latch.

Satisfied all reads with the last value written. Blocks readers until at least one value is written.

//[chips.flx]
chip latch[T]
  connector io
    pin inp: %<T
    pin out: %>T
{
   var x = read io.inp;
   device w = fetch &x;
   device r = store &x;
   circuit
     wire io.inp to r.inp
     wire io.out to w.out
   endcircuit
}

Serialise.

Read values in sequence from a sequence of channels, write each one out on a single channel. Repeat. The input channels are fixed by supplying them as an argument.

//[chips.flx]
chip serialise_chan_list[T] (a: list[%<T])
 connector io
   pin out: %>T
{
  while true do
    var current = a;
next:>
    match current with
    | Cons (h,t) =>
      var x = read h;
      write (io.out, x);
      current = t;
      goto next;
    | Empty => ;
    endmatch;
  done
}

typedef iopair_t[D,C] = (inp: %<D, out: %>C);

// transducer
typedef iochip_t[D,C] = iopair_t[D,C] -> 1 -> 0;

// sink
typedef ichip_t[T] = (inp: %<T) -> 1 -> 0;

// source
typedef ochip_t[T] = (out: %>T) -> 1 -> 0;

chip pipeline_list[T] (a: list[iochip_t[T,T]])
  connector io
    pin inp: %<T
    pin out: %>T
{
  proc aux (lst:list[iochip_t[T,T]]) (inp: %<T) {
    match lst with
    | h1 ! h2 ! tail =>
      var inchan,outchan = mk_ioschannel_pair[T]();
      spawn_fthread$  h1 (inp=inp, out=outchan);
      aux (h2!tail) inchan;
    | h1 ! _ =>
      spawn_fthread$  h1 (inp=inp, out=io.out);
    | Empty =>
      spawn_fthread$ buffer (inp=io.inp, out=io.out);
    endmatch;
  }
  aux a io.inp;
}

// This loops, but only by repeatedly spawning
// the alternative set. The alternatives are restricted
// to a single read on each iteration. The chips are
// respawned because they might be locked up, in which
// case the whole thing locks up.
//
// NOTE: if one of the alternatives starts, and does not
// read the input, everything locks up. This is because
// the implementation ACTUALLY progresses serially.
//
// this COULD be fixed by adding a buffer to the front of
// each. Actually better, add a one shot source based
// on the input.
chip tryall_list[D,C with Str[D]] (a: list[iochip_t[D,C]])
  connector io
    pin inp: %<D
    pin out: %>C
{
  while true do
    var x = read io.inp;
    //println$ "Tryall read " + a.len.str + " alternatives: " + x.str;
    //var counter = 1;
    for h in a do
      //println$ "Trying alternative #" + counter.str + "/"+a.len.str;
      var lin,lout = mk_ioschannel_pair[D]();
      spawn_fthread (h (inp=lin, out=io.out));
      //println$ "Tryall_list write " + lout.address.str;
      write (lout,x);
    done
  done
}

Deref

This version spawns a clone of p for each input. In particular it delays the spawn until there is an input.

//[chips.flx]
chip deref_each_read[D,C] (p:&iochip_t[D,C])
  connector io
    pin inp: %<D
    pin out: %>C
{
  while true do
    var x = read io.inp;
    var rinp,rout = mk_ioschannel_pair[D]();
    spawn_fthread ((*p) (inp=rinp, out=io.out));
    // println$ "Deref_each_read: write " + io.out.address.str;
    write (rout,x);
  done
}

chip deref_first_read[D,C] (p:&iochip_t[D,C])
  connector io
    pin inp: %<D
    pin out: %>C
{
  var x = read io.inp;
  var rinp,rout = mk_ioschannel_pair[D]();
  spawn_fthread ((*p) (inp=rinp, out=io.out));
  write (rout,x);
  while true do
    x = read io.inp;
    write (rout,x);
  done
}

Epsilon

Identity chip.

//[chips.flx]
chip epsilon[T]
  connector io
   pin inp: %<T
   pin out: %>T
{
  while true do
    var x = read io.inp;
    //println$ "Epsilon: write " + io.out.address.str;
    write (io.out, x);
  done
}

Optional matcher.

Matches given matcher if possible and epsilon. Note the epsilon match is ALWAYS output!

//[chips.flx]
chip optional[T] (p:iochip_t[T,T])
  connector io
    pin inp: %<T
    pin out: %>T
{
  device both = tryall_list ([
    p,
    epsilon[T]
  ]);
  circuit
    wire io.inp to both.inp
    wire io.out to both.out
  endcircuit
}

One or more matcher

//[chips.flx]

chip oneormore_matcher[T] (A:iochip_t[T,T])
connector chans
  pin inp: %<T
  pin out: %>T
{
 device As = oneormore_matcher A;
 device As2 = pipeline_list (A,As).list;
 device Ass = tryall_list (A, As2).list;
 circuit
   wire chans.inp to Ass.inp
   wire chans.out to Ass.out
 endcircuit
}

Zero or more matcher

//[chips.flx]

chip zeroormore_matcher[T] (A:iochip_t[T,T])
connector chans
  pin inp: %<T
  pin out: %>T
{
 device As = oneormore_matcher A;
 device Ass = tryall_list (epsilon[T], As).list;
 circuit
   wire chans.inp to Ass.inp
   wire chans.out to Ass.out
 endcircuit
}
//[chips.flx]
} // end class BaseChips