{- |
Module: Ion
Description: Top-level Ion module
Copyright: (c) 2015 Chris Hodapp

Ion is a Haskell EDSL for concurrent, realtime, embedded programming.
It performs compile-time scheduling, and produces scheduling code
with constant memory usage and deterministic execution (i.e. no
possibility for divergence).

It interfaces with another, more powerful EDSL, <http://ivorylang.org/
Ivory>, to perform code generation.  Ivory is responsible for all the
code generation to perform the scheduling.  One may also embed general
Ivory effects in an Ion spec with few restrictions, however, it does
very little to enforce constant memory usage or deterministic code
here.

Ion generates scheduling code which must be called at regular clock
ticks (i.e. from a timer interrupt).  The interval of these clock
ticks establishes the *base rate* of the system.  All scheduled events
in the system take place relative to this base rate, defined in terms
of 'period' (interval of repetition) and 'phase' (position within that
interval).

This functionality is expressed in the 'Ion' monad - in large part to
allow composition and modularity in expressing tightly-scheduled
functionality.  In addition, it has functions like 'newProc' and
'newArea' which define uniquely-named C functions and globals.  The
purpose of these is to allow that same compositional when working with
Ivory definitions that are parametrized and may be instantiated
multiple times.

For instance, when dealing with functions that return via asynchronous
callbacks or interrupts - a common thing on embedded systems - one
must generally work in continuation-passing style.  This simplifies
the process of creating a reusable pattern for a use-case like:

1. Transmit instruction @I@ over SPI. Wait to receive 2 bytes.
2. In a callback: Check that result for being an error condition.  If
an error, call error handler function @E@.  If successful, transmit
instruction @I2@ and wait to receive 2 bytes.
3. In a callback: Check for error and call @E@ if needed.  If successful,
combine result into some composite value, and call success handler @S@
with that value.

and then parametrizing this whole definition over instructions @I@ and
@I2@, error handler @E@, and success handler @S@.  This definition
then could be parametrized over multiple different instructions, and
all of these chained together (e.g. via @(=<<)@) to create a larger
sequence of calls passing control via CPS.

Ion was heavily inspired by another EDSL,
<https://hackage.haskell.org/package/atom Atom>. It started as an Atom
re-implementation which had other backends, rather than generating C
code directly (as Atom does).  However, Ion has diverged somewhat, and
still does not have many things from Atom, such as synchronous
variable access, run-time checks on execution time, various
compile-time sanity checks, traces, or most of its standard library.

To-do items:

   * Continue writing documentation and examples!
   * Get some unit tests for things that I am prone to breaking.
   * It *still* does not handle 'minimum' phase.
   * This could use a way to 'invert' a phase, and run at every phase but
the ones noted.
   * I need to convert over the 'schedule' function in Scheduling.hs in Atom.
   * Atom treats everything within a node as happening at the same time, and I
do not handle this yet, though I rather should.  This may be complicated - I
may either need to process the Ivory effect to look at variable references, or
perhaps add certain features to the monad.
   * Atom had a way to express things like rising or falling edges, and
debouncing.  How possible is this to express?
   * Right now one can only pass variables to an Ion by way of a Ref or some
derivative, and those must then be dereferenced inside of an 'ivoryEff' call.
Is this okay?  Should we make this more flexible somehow?  (I feel like Atom
did it similarly, with V & E.)
   * Pretty-printing the schedule itself (as Atom does) would probably be a
good idea.
   * Consider the case where one puts a condition on a node, and that node
has many sub-nodes across various delays.  Now, suppose that that condition
becomes false somewhere in the middle of those delays.  Is the entire node
blocked from taking effect, or does it partially take effect?  When is the
condition considered as being evaluated?  Right now it is evaluated at every
single sub-node that inherits it.  I consider this to be a violation of how
Ion should operate - synchronously and atomically.
   * Could 'ivoryEff' meaningfully return a value to 'Ion' rather than ()?
   * Would it be possible to make a CFG for the continuation-passing style
arrangements?  (Might Ivory have to handle this?)
   * Runtime check: Schedule function being called twice in one clock tick.
   * Runtime check: Schedule function never called in a clock tick.
   * Runtime check: Schedule function hasn't returned yet when next clock
tick occurs (i.e. schedule function takes too long).
   * Runtime check: Compute percent utilization, time-wise, in schedule
function.
   * Compile-time check: Same period and phase occupied.  (Atom would throw
a compile-time error when this happened.)

-}

module Ivory.Language.Ion (
    -- * Base types
    Base.Ion
  , CPS.IonCont
  
    -- * Code generation
  , Code.IonExports(..)
  , Code.ionDef

    -- * Operators
    
    -- ** Compositional
    -- | These functions all have @'Ion' a -> 'Ion' a@ (or similar) at the
    -- end of their type, and that is because they are meant to be
    -- nested by function composition. For instance:
    --
    -- @
    -- 'ion' "top_level" $ do
    --     'ion' "sub_spec" $ 'period' 100 $ do
    --          'ion' "phase0" $ 'phase' 0 $ do
    --              -- Everything here inherits period 100, phase 0, and
    --              -- a new path "top_level.sub_spec.phase0".
    --          'phase' 20 $ 'phase' '30' $ do
    --              -- Everything here inherits period 100, and phase 30
    --          'phase' 40 $ 'cond' (return true) $ do
    --              -- Everything here inherits period 100, phase 40, and
    --              -- a (rather vacuous) condition
    --          'disable' $ 'phase' 50 $ do
    --              -- This is all disabled.
    -- @
    --
    -- Note that more inner bindings override outer ones in the case
    -- of 'phase', 'delay', 'period', and 'subPeriod'.  Applications
    -- of 'cond' combine with each other as a logical @and@.
    -- Applications of 'disable' are idempotent.
  , Operators.ion
  , Operators.phase
  , Operators.delay
  , Operators.period
  , Operators.subPeriod
  , Operators.cond
  , Operators.disable
    
    -- ** Memory & Procedures
  , Operators.newName
  , Operators.newProc
  , Operators.newProcP
  , Operators.area'
  , Operators.areaP'
  , Operators.newArea
  , Operators.newAreaP
    
    -- ** Effects
  , Operators.ivoryEff
    
    -- ** Utilities
  , Operators.timer
  , Operators.startTimer
  , Operators.stopTimer
  , Operators.getPhase
  , Operators.adapt_0_1
  , Operators.adapt_1_0
  , Operators.adapt_0_2
  , Operators.adapt_2_0
  , Operators.adapt_0_3
  , Operators.adapt_3_0
  , Operators.adapt_0_4
  , Operators.adapt_4_0
  , Operators.adapt_0_5
    -- Yes, the 'utilities' aren't in module Util. Whatever.

    -- ** CPS
  , CPS.accum
    
  ) where

import qualified Ivory.Language.Ion.Base as Base
import qualified Ivory.Language.Ion.Code as Code
import qualified Ivory.Language.Ion.CPS as CPS
import qualified Ivory.Language.Ion.Operators as Operators
import qualified Ivory.Language.Ion.Util as Util