forked from konn/LazyK
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSExp.hs
More file actions
114 lines (95 loc) · 3.33 KB
/
SExp.hs
File metadata and controls
114 lines (95 loc) · 3.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
{-# LANGUAGE OverloadedStrings, QuasiQuotes #-}
module SExp (lisp, scm, evalLispWithEnv, evalLispDefault) where
import Text.Parsec
import Text.Parsec.String hiding (Parser)
import Text.Parsec.Combinator
import Control.Applicative hiding (many, (<|>), empty)
import Data.Maybe
import Data.Map hiding (map)
import Control.Monad
import Prelude hiding (lookup)
import Control.Arrow hiding (app)
import Debug.Trace
import Language.Haskell.TH.Quote
import Data.Char
import Language.Haskell.TH hiding (Name, letE)
import LazyK
type Parser = Parsec String (Map String Int)
ident :: Bool -> Parser Name
ident incr = do
base <- (:) <$> letter <*> many alphaNum
col <- maybe 0 (if incr then succ else id) . lookup base <$> getState
updateState (insert base col)
return $ Name base col
lisp = val <|> try lambda <|> try letE <|> try cons <|> app
lambda :: Parser Lisp
lambda = parens $ do
symbol "lambda" <* notFollowedBy alphaNum
vars <- parens $ ident True `sepBy` spaces
code <- lisp
updateState (flip (foldr (uncurry insert)) (map (unName &&& pred . identifier) vars))
return $ foldr Abs code vars
app :: Parser Lisp
app = parens $ lisp `chainl1` (AppL <$ spaces)
reserved :: String -> Parser String
reserved name = do
num <- fromMaybe 0 . lookup name <$> getState
when (num > 0) $ fail "reserved value is overwritten"
string name <* notFollowedBy alphaNum
val :: Parser Lisp
val = try (Prim S <$ reserved "S")
<|> try (Prim K <$ reserved "K")
<|> try (Prim I <$ reserved "I")
<|> Var <$> ident False
cons :: Parser Lisp
cons = parens $ do
ls <- lisp `sepEndBy` spaces
symbol "."
l2 <- lisp
return $ foldr (AppL . (AppL (Var "cons"))) l2 ls
defs :: Parser [(Name, Lisp)]
defs = many $ parens ((,) <$> ident True <* spaces <*> lisp)
letE :: Parser Lisp
letE = parens $ do
symbol "let" <* notFollowedBy alphaNum
spaces
dic <- parens defs
spaces
body <- lisp
let (names, decs) = unzip dic
lam = foldr Abs body names
app = foldl AppL lam decs
return app
env :: Parser (Lisp -> Lisp)
env = do
table <- defs
return $ \body -> foldr (\(name, dec) b -> AppL (Abs name b) dec) body table
defDics :: Map String Int
defDics = fromList [("S", 0), ("K", 0), ("I", 0)]
enclose :: Name -> Lisp -> Lisp -> Lisp
enclose name dec body = AppL (Abs name dec) body
evalLispWithEnv :: String -> String -> Maybe Expr
evalLispWithEnv envSrc body =
let (run, st) = either (error.show) id $ runParser ((,) <$> env <*> getState) empty "env" envSrc
ans = either (error . show) (Just . run) $ runParser (spaces *> lisp <* eof) st "src" body
ans' = flip (foldr (uncurry enclose))
[(Name "S" 0, Prim S), (Name "K" 0, Prim K), (Name "I" 0, Prim I)] <$> ans
in translate =<< ans
evalLispDefault :: String -> IO (Maybe Expr)
evalLispDefault src = do
env <- readFile "env.scm"
return $ evalLispWithEnv env src
scm :: QuasiQuoter
scm = QuasiQuoter { quoteExp = parseScmExp
, quotePat = parseScmPat
, quoteDec = undefined
, quoteType = undefined
}
parseScmExp :: String -> ExpQ
parseScmExp src = do
ans <- fromJust <$> runIO (evalLispDefault $ dropWhile isSpace src)
dataToExpQ (const Nothing) ans
parseScmPat :: String -> PatQ
parseScmPat src = do
ans <- fromJust <$> runIO (evalLispDefault $ dropWhile isSpace src)
dataToPatQ (const Nothing) ans