module Language.Sifflet.Export.ToScheme
(SExpr(..)
, Atom(..)
, Indent
, Exporter
, SchemeOptions(..)
, defaultSchemeOptions
, exprToSExpr
, functionNameToSchemeName
, valueToSExpr
, exprToSchemeRepr
, exprToSchemePretty
, exprToScheme
, inl
, sepLines2
, functionsToSExprs
, functionsToPrettyScheme
, defToSExpr
, exportScheme
)
where
import System.Sifflet.Paths
import Data.Number.Sifflet
import Graphics.UI.Sifflet.Types (VPUI(..))
import Language.Sifflet.Expr
import Language.Sifflet.Export.Exporter
import Language.Sifflet.Util
import Text.Sifflet.Pretty
import Text.Sifflet.Repr
data SExpr = SAtom Atom | SList [SExpr]
deriving (Eq, Show)
data Atom = SFloat Double
| SInt Integer
| SSymbol String
| SString String
| SChar Char
| SBool Bool
| SFunction Function
deriving (Eq, Show)
type Indent = Int
data SchemeOptions =
SchemeOptions { defineWithLambda :: Bool
}
deriving (Eq, Show)
defaultSchemeOptions :: SchemeOptions
defaultSchemeOptions = SchemeOptions {defineWithLambda = False}
flattish :: [SExpr] -> Bool
flattish sexprs =
case sexprs of
[] -> True
x:xs -> atom x && flattish xs
atom :: SExpr -> Bool
atom sexpr =
case sexpr of
SAtom _ -> True
SList [] -> True
_ -> False
exprToSExpr :: Expr -> SExpr
exprToSExpr expr =
case expr of
EUndefined ->
SAtom (SSymbol "*sifflet-undefined*")
ESymbol (Symbol str) ->
SAtom (SSymbol (functionNameToSchemeName str))
EBool b -> valueToSExpr (VBool b)
EChar c -> valueToSExpr (VChar c)
ENumber n -> valueToSExpr (VNumber n)
EString s -> valueToSExpr (VString s)
EIf cond action altAction ->
SList [SAtom (SSymbol "if"), exprToSExpr cond,
exprToSExpr action, exprToSExpr altAction]
EList exprs ->
SList (SAtom (SSymbol "list") : (map exprToSExpr exprs))
ELambda (Symbol x) body ->
SList [SAtom (SSymbol "lambda"), SList [SAtom (SSymbol x)],
exprToSExpr body]
ECall fsym args ->
SList (exprToSExpr (ESymbol fsym) : map exprToSExpr args)
_ -> errcats ["exprToSExpr: extended expr:", show expr]
functionNameToSchemeName :: String -> String
functionNameToSchemeName name =
case name of
"mod" -> "modulo"
"add1" -> "sifflet-add1"
"sub1" -> "sifflet-sub1"
"==" -> "equal?"
"/=" -> "sifflet-not-equal?"
"null" -> "null?"
"head" -> "car"
"tail" -> "cdr"
":" -> "cons"
_ -> name
valueToSExpr :: Value -> SExpr
valueToSExpr value =
case value of
VList vs ->
SList [SAtom (SSymbol "quote"), SList (map valueToSExpr vs)]
_ ->
SAtom (case value of
VBool b -> SBool b
VChar c -> SChar c
VNumber (Exact i) -> SInt i
VNumber (Inexact x) -> SFloat x
VString s -> SString s
VFun f -> SFunction f
VList _ ->
error ("valueToSExpr: Impossible! " ++
"We can't have VList here!")
)
exprToSchemeRepr :: Expr -> String
exprToSchemeRepr = repr . exprToSExpr
exprToSchemePretty :: Expr -> String
exprToSchemePretty = pretty . exprToSExpr
exprToScheme :: Expr -> String
exprToScheme = exprToSchemePretty
instance Repr SExpr where
repr sexpr =
case sexpr of
SAtom satom ->
case satom of
SFloat x -> show x
SInt i -> show i
SSymbol name -> name
SString str -> show str
SChar char -> show char
SBool False -> "#f"
SBool True -> "#t"
SFunction (Function mname _ _ _) ->
case mname of
Nothing ->
error "SExpr/repr: cannot repr unnamed function"
Just name ->
functionNameToSchemeName name
SList exprs ->
"(" ++ unwords (map repr exprs) ++ ")"
instance Pretty SExpr where
pretty = prettyLoop 0
prettyLoop :: Indent -> SExpr -> String
prettyLoop ind sexpr =
case sexpr of
SAtom _ -> repr sexpr
SList xs ->
if flattish xs
then repr sexpr
else
case xs of
[] -> error "prettyLoop: non-flattish xs cannot be []."
[SAtom (SSymbol "if"), _, _, _] ->
displayList2 ind (ind + 4) xs
[SAtom (SSymbol "define"), _, _] ->
displayList2 ind (ind + 4) xs
[SAtom (SSymbol "lambda"), _, _] ->
displayList2 ind (ind + 4) xs
SAtom (SSymbol name) : args ->
case args of
[] ->
displayList1 ind (ind + length name + 2) xs
_ ->
displayList2 ind (ind + length name + 2) xs
_ -> displayList1 ind (ind + 1) xs
inl :: Int -> String
inl ind = "\n" ++ replicate ind ' '
displayList1 :: Indent -> Indent -> [SExpr] -> String
displayList1 ind ind' xs =
case xs of
[] -> error "displayList1: empty list"
x:xs' -> "(" ++
prettyLoop ind x ++
displayTail ind' xs'
displayList2 :: Indent -> Indent -> [SExpr] -> String
displayList2 ind ind' xs =
case xs of
x0:x1:xs' -> "(" ++ prettyLoop ind x0 ++
" " ++ prettyLoop ind' x1 ++
displayTail ind' xs'
_ -> error "displayList2: list is too short"
displayTail :: Indent -> [SExpr] -> String
displayTail ind xs =
case xs of
[] -> ")"
x:[] -> inl ind ++ prettyLoop ind x ++ ")"
x:xs' -> inl ind ++ prettyLoop ind x ++ displayTail ind xs'
functionsToSExprs :: SchemeOptions -> Functions -> [SExpr]
functionsToSExprs options (Functions fs) =
map (defToSExpr options . functionToDef) fs
functionsToPrettyScheme :: SchemeOptions -> Functions -> String
functionsToPrettyScheme options =
sepLines2 . map pretty . functionsToSExprs options
defToSExpr :: SchemeOptions -> FunctionDefTuple -> SExpr
defToSExpr options (name, args, _atypes, _rtype, body) =
let asym = SAtom . SSymbol
sdefine = asym "define"
sname = asym name
sbody = exprToSExpr body
in case args of
[] -> SList [sdefine, sname, sbody]
_:_ ->
let argAtoms = map asym args
in if defineWithLambda options
then let slambda = asym "lambda"
sargs = SList argAtoms
slambdaArgsBody = SList [slambda, sargs, sbody]
in SList [sdefine, sname, slambdaArgsBody]
else let snameArgs = SList (sname : argAtoms)
in SList [sdefine, snameArgs, sbody]
exportScheme :: VPUI -> SchemeOptions -> Exporter
exportScheme vpui options functions path =
let header = ";;; File: " ++ path ++ "\n" ++
";;; Generated by the Sifflet->Scheme exporter.\n" ++
"\n" ++
";;; You may need to insert the contents of sifflet.scm,\n" ++
";;; from the Sifflet installation directory," ++
" into this file\n"
in do
lib <- readLibFile vpui "sifflet.scm" path
writeFile path
(sepLines2 [header,
functionsToPrettyScheme options functions,
lib])