module Eval (evaluate) where

import Debug.Trace (trace)

import ParseTree
import Control.Monad (mapM, zipWithM)
import Control.Monad.List
import Control.Monad.Error
import Data.Maybe (fromJust)




              
-- This function is essentially lazy to deal with non-determinism.
-- 
evaluate :: ParseTree a -> SymbolTree a -> ListT (Either String) (SymbolTree a)
evaluate declarations expression = 
    case expression of
      SNode (Constructor c a) es -> 
          do vs <- mapM (evaluate declarations) es 
             return $ SNode (Constructor c a) vs
      SNode (Variable _ _) _ ->
          throwError "Extra Variable!" -- failed
      SNode (Function f a) es ->
          do vs <- mapM (evaluate declarations) es            
             (ListT $ Right declarations) >>= evaluateFunctionCall declarations (SNode (Function f a) vs) 
    where 
      evaluateFunctionCall :: ParseTree a -> SymbolTree a -> Declaration a -> ListT (Either String) (SymbolTree a)
      evaluateFunctionCall declarations f (Decl (l,r,w) n) = 
          case patternMatch l f of 
            Just bind -> 
                let r'  = substitute r bind 
                    w'  = map (\(wl,wr)->(wl,evaluate declarations (substitute wr bind))) w 
                    b'  = foldr (\(wl,wvs) bss-> 
                                           do wv <- wvs
                                              bd <- return $ fromJust(patternMatch wl wv)
                                              bs <- bss 
                                              return $ bd++bs) (return []) w'
                in do bind' <- b'
                      evaluate declarations (substitute r' bind')  
            Nothing -> ListT $ Right []





      