{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Expr where
import Prelude hiding ((.), id)
import Control.Category
import Data.Data (Data)
import qualified Data.ByteString.Lazy.Char8 as B8
import qualified Language.Sexp as Sexp
import Language.SexpGrammar
import Language.SexpGrammar.Generic
import GHC.Generics
newtype Ident = Ident String
deriving (Show, Generic)
data Expr
= Var Ident
| Lit Int
| Add Expr Expr
| Mul Expr Expr
| Neg Expr
| Inv Expr
| IfZero Expr Expr (Maybe Expr)
| Apply [Expr] String Prim -- inconvenient ordering: arguments, useless annotation, identifier
deriving (Show, Generic)
data Prim
= SquareRoot
| Factorial
| Fibonacci
deriving (Eq, Enum, Bounded, Data, Show, Generic)
instance SexpIso Prim
instance SexpIso Ident where
sexpIso = with (\ident -> ident . symbol')
instance SexpIso Expr where
sexpIso = match
$ With (\var -> var . sexpIso)
$ With (\lit -> lit . int)
$ With (\add -> add . list (el (sym "+") >>> el sexpIso >>> el sexpIso))
$ With (\mul -> mul . list (el (sym "*") >>> el sexpIso >>> el sexpIso))
$ With (\neg -> neg . list (el (sym "negate") >>> el sexpIso))
$ With (\inv -> inv . list (el (sym "invert") >>> el sexpIso))
$ With (\ifz -> ifz . list (el (sym "cond") >>> props ( Kw "pred" .: sexpIso
>>> Kw "true" .: sexpIso
>>> Kw "false" .:? sexpIso )))
$ With (\app -> app . list
(el (sexpIso :: SexpG Prim) >>> -- Push prim: prim :- ()
el (kw (Kw "args")) >>> -- Recognize :args, push nothing
rest (sexpIso :: SexpG Expr) >>> -- Push args: args :- prim :- ()
swap >>> -- Swap: prim :- args :- ()
push "dummy" >>> -- Push "dummy": "dummy" :- prim :- args :- ()
swap -- Swap: prim :- "dummy" :- args :- ()
))
$ End
exprGrammar :: SexpG Expr
exprGrammar = sexpIso
test :: String -> SexpG a -> (a, String)
test str g = either error id $ do
e <- decodeWith g (B8.pack str)
sexp' <- genSexp g e
return (e, B8.unpack (Sexp.encode sexp'))
-- > test "(cond 1 (+ 42 10) (* 2 (* 2 2)))"
-- (IfZero (Lit 1) (Add (Lit 42) (Lit 10)) (Mul (Lit 2) (Mul (Lit 2) (Lit 2))),"(cond 1 (+ 42 10) (* 2 (* 2 2)))")