packages feed

technique-0.2.5: lib/Technique/Internal.hs

{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}

-- I generally try to avoid modules full of (only) types but these are here
-- so the can be shared in both Technique.Translate and Technique.Builtins.

-- |
-- Builing blocks for the translation stage of the compiler.
module Technique.Internal where

import Core.Text
import Data.DList
import Technique.Language
import Technique.Quantity

-- FIXME ??? upgrade to named IVar
newtype Promise = Promise Value

-- |
-- The resolved value of eiter a literal or function applicaiton, either as
-- that literal, the expression, or as the result of waiting on the variable
-- it was assigned to.
--
-- Need names? Science names newly discovered creatures in Latin. I don't
-- speak Latin, but neither does anyone else so we can just make words up.
-- Yeay! (The lengths some people will go to in order to avoid qualified
-- imports is really impressive, isn't it?)
data Value
  = Unitus
  | Literali Rope
  | Quanticle Quantity
  | Tabularum [(Rope, Value)]
  | Parametriq [Value]
  deriving (Eq, Show)

-- |
-- The internal representation of a Procedure, with ambiguities resolved.
--
-- We landed on Subroutine as the name of translated user-defined Procedure.
--
-- Procedures which are actually fundamental [in the context of the domain
-- specific language] represented by builtin IO actions which we call
-- Primatives.
--
-- The first constructor, Unresolved, is for the first stage pass through the
-- translate phase when we are still accumulating definitions, thereby
-- allowing for the forward use of not-yet-defiend procedures that will be
-- encountered in the same scope.

-- Didn't want to call this "function" because that means something in
-- functional programming and in programming language theory and this isn't
-- it. Other alternatives considered include Instance (the original name,
-- but we've reserved that to be used when instantiating a procedure at
-- runtime), Representation, and Internal. Subroutine is ok.
data Function
  = Unresolved Identifier
  | Subroutine Procedure Step
  | Primitive Procedure (Step -> IO Value)

functionName :: Function -> Identifier
functionName func = case func of
  Unresolved name -> name
  Subroutine proc _ -> procedureName proc
  Primitive prim _ -> procedureName prim

instance Show Function where
  show func =
    let name = fromRope (unIdentifier (functionName func))
     in case func of
          Unresolved _ -> "Unresolved \"" ++ name ++ "\""
          Subroutine _ step -> "Subroutine \"" ++ name ++ "\": " ++ show step
          Primitive _ _ -> "Primitive \"" ++ name ++ "\""

instance Eq Function where
  (==) f1 f2 = case f1 of
    Unresolved i1 -> case f2 of
      Unresolved i2 -> i1 == i2
      _ -> False
    Subroutine proc1 step1 -> case f2 of
      Subroutine proc2 step2 -> proc1 == proc2 && step1 == step2
      _ -> False
    -- this is weak, but we can't compare Haskell functions for equality so if
    -- the Procedures are the same then we assume the Primitives are.

    Primitive proc1 _ -> case f2 of
      Primitive proc2 _ -> proc1 == proc2
      _ -> False

newtype Name = Name Rope -- ??? upgrade to named IVar := Promise ???
  deriving (Eq, Show)

-- |
-- Names. Always needing names. These ones are from original work when we
-- envisioned technique as a shallow embedding of a domain specific
-- language implemented in Haskell. Comments describing constructors are
-- taken from a suggestion by Oleg Kiselyov on page 23 of his course "Typed
-- Tagless Final Interpreters" that the constructors of a simply typed
-- lambda calculus in this style could be considered a "minimal
-- intuitionistic logic" which is absolutely fabulous.

-- While it probably would work to put an Asynchronous into a Tuple list,
-- it's not valid from the point of view of the surface language syntax.
data Step
  = Known Offset Value -- literals ("axioms")
  | Bench Offset [(Label, Step)]
  | Depends Offset Name -- block waiting on a value ("reference to a hypothesis denoted by a variable")
  | NoOp
  | Tuple Offset [Step]
  | Asynchronous Offset [Name] Step -- assignment (ie lambda, "implication introduction"
  | Invocation Offset Attribute Function Step -- function application ("implication elimination") on a [sub] Procedure
  | Nested Offset (DList Step)
  -- assumption axiom?
  -- weakening?
  deriving (Eq, Show)

instance Located Step where
  locationOf step = case step of
    Known o _ -> o
    Bench o _ -> o
    Depends o _ -> o
    NoOp -> -2
    Tuple o _ -> o
    Asynchronous o _ _ -> o
    Invocation o _ _ _ -> o
    Nested o _ -> o

instance Semigroup Step where
  (<>) = mappend

instance Monoid Step where
  mempty = NoOp
  mappend NoOp s2 = s2
  mappend s1 NoOp = s1
  mappend (Nested o1 list1) (Nested _ list2) = Nested o1 (append list1 list2)
  mappend (Nested o1 list1) s2 = Nested o1 (snoc list1 s2)
  mappend s1 (Nested _ list2) = Nested (locationOf s1) (cons s1 list2)
  mappend s1 s2 = Nested (locationOf s1) (snoc (singleton s1) s2)