packages feed

cap-1.0: src/Language/Cap/Interpret/Program.hs

module Language.Cap.Interpret.Program (interpret) where

import Language.Cap.Interpret.Parse
import Language.Cap.Debug.Trace

import Data.Map (Map(..))
import qualified Data.Map as M
import Data.Maybe
import Data.List
import System.IO.Unsafe


-- | Substitutions are mappings from variable names to a graph to substitute in.
type Substitution = Map String NodeName

-- | Checks whether a term is simply a variable.
isVariable :: Term -> Bool
isVariable (TVariable _) = True
isVariable _ = False

-- | Fetches the variable name from a variable term.
variableName :: Term -> String
variableName (TVariable x) = x

-- | Constructs a graph for a term using a particular substitution.  No
--   evaluation is performed.
graph :: NodeName -> Term -> Substitution -> Graph
graph n (TAtom x) _ = M.singleton n (Atom x)
graph n (TVariable x) subs =
  M.singleton n (Indirection (fromJust (M.lookup x subs)))
graph n (TApplication (TVariable i) (TVariable j)) subs =
  M.singleton n (Application (fromJust (M.lookup i subs))
                             (fromJust (M.lookup j subs)))
graph n (TApplication (TVariable i) j) subs =
  M.insert n
           (Application (fromJust (M.lookup i subs)) ('a':n))
           (graph ('a':n) j subs)
graph n (TApplication i (TVariable j)) subs =
  M.insert n
           (Application ('f':n) (fromJust (M.lookup j subs)))
           (graph ('f':n) i subs)
graph n (TApplication i j) subs =
  M.insert n
           (Application ('f':n) ('a':n))
           (M.union ig jg)
  where
    ig = graph ('f':n) i subs
    jg = graph ('a':n) j subs

-- | Attempts to match a node in the graph against a pattern.
match :: Graph -> NodeName -> Term -> Maybe Substitution
match t n (TAtom a) =
  case nodeValue t n of
    Just (Atom a') -> if a == a' then Just M.empty
                                 else Nothing
    _              -> Nothing
match t n (TApplication i j) =
  case nodeValue t n of
    Just (Application i' j') ->
      do is <- if isVariable i then Just (M.singleton (variableName i) i')
                               else match t (nodeLast t i') i
         js <- if isVariable j then Just (M.singleton (variableName j) j')
                               else match t (nodeLast t j') j
         return $ M.union is js
    _ -> Nothing

-- | Evaluates the a term based on a given program.  Returns a trace of the
--   completed evaluation
interpret :: Program -> Term -> Graph
interpret p t = compute p (graph "" t (M.fromList []))

-- | Evaluates the program by continuously attempting to match un-reduced
--   computations against rewrite rules.
compute :: Program -> Graph -> Graph
compute p g = if isJust computableNode
                  then compute p (M.union g (graph ('r':redex) term subs))
                  else g
                where
                  computableNode = findRedex p g
                
                  (redex,term,subs) = fromJust computableNode

-- | Attempts to find a reducable expression, and the rewrite rule that it may
--   be reduced with.
findRedex :: Program -> Graph -> Maybe (NodeName,Term,Substitution)
findRedex p g =
  first (tryRules p g)
        [n | n <- nodes,    (not $ isIndirection $ fromJust $ nodeValue g n)
                         && not (('r':n) `elem` nodes)]
  where
    nodes = sortBy outerMostLeftMostFirst $ necessaryNodes g

-- | Finds all nodes that may need to be reduced to complete construction of
--   the trace
necessaryNodes :: Graph -> [NodeName]
necessaryNodes g =
  nub $ necessaryNodes' g ""
  where
    necessaryNodes' :: Graph -> NodeName -> [NodeName]
    necessaryNodes' g n =
      if ('r':n) `elem` allNodes g
        then necessaryNodes' g ('r':n)
        else
          case nodeValue g n of
            Just (Application i j)
              -> n:(necessaryNodes' g i ++ necessaryNodes' g j)
            Just (Atom a)
              -> [n]
            Just (Indirection x)
              -> necessaryNodes' g x

-- | Establishes an outermost leftmost first order on nodes
outerMostLeftMostFirst :: NodeName -> NodeName -> Ordering
outerMostLeftMostFirst x y =
  outerMostLeftMostFirst' (reverse x) (reverse y)
  where
    outerMostLeftMostFirst' [] [] = EQ
    outerMostLeftMostFirst' [] ('r':ys) = GT
    outerMostLeftMostFirst' [] (_:ys) = LT
    outerMostLeftMostFirst' ('r':xs) [] = LT
    outerMostLeftMostFirst' (_:xs) [] = GT
    outerMostLeftMostFirst' ('r':xs) ('r':ys) = outerMostLeftMostFirst' xs ys
    outerMostLeftMostFirst' ('f':xs) ('f':ys) = outerMostLeftMostFirst' xs ys
    outerMostLeftMostFirst' ('a':xs) ('a':ys) = outerMostLeftMostFirst' xs ys
    outerMostLeftMostFirst' ('r':xs) (_:ys) = LT
    outerMostLeftMostFirst' (_:xs) ('r':ys) = GT
    outerMostLeftMostFirst' ('f':xs) ('a':ys) = LT
    outerMostLeftMostFirst' ('a':xs) ('f':ys) = GT

-- | Tries each rewrite rule on a given unreduced application to find out if
--   it's a redex.
tryRules :: Program -> Graph -> NodeName -> Maybe (NodeName,Term,Substitution)
tryRules [] g n = Nothing
tryRules ((Rule pattern term):rs) g n = 
  if isJust subs then Just (n,term,fromJust subs)
                 else tryRules rs g n
  where
    subs = match g n pattern

-- | Finds the first item in a list for which f does not return Nothing.-}
first :: (a -> Maybe b) -> [a] -> Maybe b
first f [] = Nothing
first f (x:xs)
  = if isJust r then r
                else first f xs
    where
      r = f x