{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE RankNTypes #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Expr where import Prelude hiding ((.), id) import Control.Category import Data.Data (Data) import Data.Semigroup import Data.Text.Lazy (Text) import Language.Sexp import Language.SexpGrammar import Debug.Trace (trace) traceIt :: Show a => a -> a traceIt a = trace ("It: " ++ show a) a newtype Ident = Ident String deriving (Show) data Expr = Var Ident | Lit Int | Add Expr Expr | Mul Expr Expr | Inv Expr | IfZero Expr Expr Expr | Apply [Expr] String Prim -- inconvenient ordering: arguments, useless annotation, identifier deriving (Show) data Pair a b = Pair a b deriving (Show) data Prim = SquareRoot | Factorial | Fibonacci deriving (Eq, Enum, Bounded, Data, Show) data Person = Person { pName :: String , pAddress :: String , pAge :: Maybe Int } deriving (Show) return [] instance SexpIso Prim instance (SexpIso a, SexpIso b) => SexpIso (Pair a b) where sexpIso = list ( -- begin list el sexpIso >>> -- consume and push first element to stack: (a :- t) el sexpIso -- consume and push second element to stack: (b :- a :- t) ) >>> $(grammarFor 'Pair) -- pop b, pop a, apply a to Pair, -- apply b to (Pair a): (Pair a b :- t) instance SexpIso Ident where sexpIso = $(grammarFor 'Ident) . symbol' instance SexpIso Expr where sexpIso = sconcat [ $(grammarFor 'Var) . sexpIso , $(grammarFor 'Lit) . int , $(grammarFor 'Add) . list (el (sym "+") >>> el sexpIso >>> el sexpIso) , $(grammarFor 'Mul) . list (el (sym "*") >>> el sexpIso >>> el sexpIso) , $(grammarFor 'Inv) . list (el (sym "invert") >>> el sexpIso) , $(grammarFor 'IfZero) . list (el (sym "cond") >>> props ( Kw "pred" .: sexpIso >>> Kw "true" .: sexpIso >>> Kw "false" .: sexpIso )) , $(grammarFor 'Apply) . list (el (sexpIso :: SexpG Prim) >>> el (kw (Kw "args")) >>> rest (sexpIso :: SexpG Expr) >>> swap >>> push "dummy" >>> swap ) ] instance SexpIso Person where sexpIso = $(grammarFor 'Person) . list ( el string' >>> props ( Kw "address" .: string' >>> Kw "age" .:? int)) sexp :: String -> Sexp sexp = either error id . parseSexp "" test :: String -> SexpG a -> (a, Text) test str g = either error id $ do sexp <- parseSexp "" str expr <- parse g sexp sexp' <- gen g expr return (expr, printSexp 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)))")