packages feed

gulcii-0.2.0.1: src/Sugar.hs

{-
    gulcii -- graphical untyped lambda calculus interpreter
    Copyright (C) 2011, 2013  Claude Heiland-Allen

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-}

{-

Sugared term parser
===================

Grammar
-------

T ::= v | '\\' v+ ('.' | '!' | '?') T | T+ | '(' T ')' | n | '[' (T (, T)*)? ']' | '{' v ':' T '}' T

-}

module Sugar(Term(..), parse, desugar) where

import Control.Applicative(Applicative, Alternative, (<|>), (<$>), (<$), (<*>), (<*), some, liftA2)
import Data.List ((\\))

import Parse
import qualified Lambda as U
import Evaluation (Strategy(..))

data Term
  = Variable String
  | Lambda Strategy [String] Term
  | Apply [Term]
  | Group Term
  | Natural Integer
  | List [Term]
  | Trace String Term Term
  deriving (Read, Show, Eq, Ord)

parse :: Parser String Term
parse = Apply <$> some parse'

parse' :: Parser String Term
parse' =  (flip Lambda <$ sym "\\" <*> some name <*> strategy <*> parse)
      <|> (Group <$ sym "(" <*> parse <* sym ")")
      <|> (Variable <$> name)
      <|> (Natural <$> integer)
      <|> (List <$ sym "[" <*> manySep (sym ",") parse <* sym "]")
      <|> (Trace <$ sym "{" <*> name <* sym ":" <*> parse <* sym "}" <*> parse)

strategy :: Parser String Strategy
strategy =  (Strict <$ sym "!") <|> (Lazy <$ sym ".") <|> (Copy <$ sym "?")

{-
A list of all possible legal variable names, need to generate fresh
variables (ie, names guaranteed to be unused in a given term).
-}

variables :: [String]
variables = [ v:vs | vs <- [] : variables, v <- ['a'..'z'] ]

{-
Desugar according to the following conventions:

  Outermost parentheses are dropped:
    M N means of (M N)

  Applications are left associative:
    M N P means (M N) P

  The body of an abstraction extends as far right as possible
    \x.M N means \x.(M N) and not (\x.M) N

  A sequence of abstractions are contracted:
    \x.\y.\z.N is abbreviated as \x y z.N

Desugar naturals with their Scott-encoding:

  0      -> \s z . z
  (1+n)  -> \s z . s (desugar n)

Desugar lists with their Scott-encoding:

  []     -> \ c n . n
  (x:xs) -> \ c n . c (desugar x) (desugar xs)
-}

desugar :: Term -> Maybe U.Term
desugar (Variable v) = Just $ U.Variable v
desugar (Lambda _ [] _) = Nothing
desugar (Lambda k [v] t) = fmap (U.Lambda k v) $ desugar t
desugar (Lambda k (v:vs) t) = fmap (U.Lambda k v) $ desugar (Lambda k vs t)
desugar (Apply []) = Nothing
desugar (Apply ts) = foldl1 (liftA2 U.Apply) (map desugar ts)
desugar (Group t) = desugar t
desugar (Natural 0) = Just $ lam "s" (lam "z" (U.Variable "z"))
desugar (Natural n) =
  let Just nn = desugar (Natural (n - 1))
      (s:z:_) = variables \\ U.freeVariablesIn nn
  in  Just $ lam s (lam z (U.Apply (U.Variable s) nn))
desugar (List []) = Just $ lam "c" (lam "n" (U.Variable "n"))
desugar (List (l:ls)) =
  let t = desugar l
      ts = desugar (List ls)
  in case (t,ts) of
    (Just t', Just ts') ->
      let (c:n:_) = (variables \\ U.freeVariablesIn t') \\ U.freeVariablesIn ts'
      in Just $ lam c (lam n (U.Apply (U.Apply (U.Variable c) t') ts'))
    _ -> Nothing
desugar (Trace k s t) = U.Trace k <$> desugar s <*> desugar t

lam :: String -> U.Term -> U.Term
lam = U.Lambda Lazy