-
Notifications
You must be signed in to change notification settings - Fork 0
/
Scheme.hs
304 lines (269 loc) · 9.06 KB
/
Scheme.hs
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
-- | A simple non-standard scheme implementation.
module Scheme where
import Data.Map as Map
import Control.Monad.State as State
-- import System.IO
import Text.Parsec
-- import Text.Parsec.String
-- import Data.List
import Data.Char
-- | Any legal Scheme token.
data SchemeValue = SchemeInteger Integer
| SchemeSymbol String
| SchemeCons HeapPointer HeapPointer
deriving (Show, Eq)
-- | A pointer to the heap.
data HeapPointer = HeapPointer Integer
| SchemeNil
deriving (Show, Eq)
instance Ord HeapPointer where
compare SchemeNil SchemeNil = EQ
compare SchemeNil b = LT
compare a SchemeNil = GT
compare (HeapPointer a) (HeapPointer b) = compare a b
-- | The type of a char parser w/ the scheme heap as user state.
-- Should return a pointer to the element parsed.
type SchemeParser = Parsec String Heap HeapPointer
data Heap = Heap { heap :: Map HeapPointer SchemeValue,
heapIndex :: HeapPointer }
deriving (Show)
newHeap :: Heap
newHeap = Heap (fromList []) (HeapPointer 0)
addToHeap :: SchemeValue -> Heap -> Heap
addToHeap v (Heap h o@(HeapPointer i)) =
Heap (Map.insert o v h) (HeapPointer (1 + i))
schemeParser :: SchemeParser
schemeParser = try schemeIntegerParser <|>
try schemeCharParser <|>
try schemeSymbolParser <|>
((char '(') >> schemeListParser)
-- | Parser for scheme S-expressions.
schemeListParser :: SchemeParser
schemeListParser = do
_ <- skipMany space
car <- try schemeIntegerParser <|>
try schemeCharParser <|>
try schemeSymbolParser <|>
try ((char '(') >> schemeListParser) <|>
schemeNilParser
case car of
SchemeNil -> return SchemeNil
(HeapPointer a) -> do
cdr <- try schemeListParser <|>
schemeNilParser
(Heap h i) <- getState
modifyState (addToHeap (SchemeCons car cdr))
return i
-- | Parser for the end of a list.
schemeNilParser :: SchemeParser
schemeNilParser = (char ')') >> return SchemeNil
-- | Parser for scheme integers.
schemeIntegerParser :: SchemeParser
schemeIntegerParser = do
(Heap h i) <- getState
skipMany space
token <- many1 digit
modifyState $ (addToHeap $ SchemeInteger . (read :: String -> Integer) $
token)
return i
-- | Parser for scheme chars.
schemeCharParser :: SchemeParser
schemeCharParser = do
(Heap h i) <- getState
skipMany space
string "#\\"
token <- letter
modifyState (addToHeap $ SchemeInteger . fromIntegral . ord $ token)
return i
-- | Parser for scheme symbols.
schemeSymbolParser :: SchemeParser
schemeSymbolParser = do
(Heap h i) <- getState
skipMany space
token <- many1 letter
modifyState (addToHeap $ SchemeSymbol token)
return i
schemeTopLevelParser :: Parsec String Heap (Heap, HeapPointer)
schemeTopLevelParser = do
e <- schemeParser
t <- getState
return (t, e)
-- | The environment containing the runtime data.
data SchemeEnvironment = SchemeEnvironment { runtimeHeap :: Heap,
environment :: HeapPointer,
continuation :: HeapPointer }
deriving (Show)
-- | The scheme runtime state monad.
type SchemeMonad a = State.State SchemeEnvironment a
-- | Allocate an atomic value on the heap.
allocSchemeAtomic :: SchemeValue -> SchemeMonad HeapPointer
allocSchemeAtomic val = do
(Heap heap' index'@(HeapPointer indexVal)) <- gets runtimeHeap
state' <- get
put $ state' { runtimeHeap = Heap (Map.insert index' val heap')
(HeapPointer $ indexVal + 1) }
return index'
-- | Dereference a heap pointer.
dereference :: HeapPointer -> SchemeMonad SchemeValue
dereference SchemeNil = return $ SchemeSymbol "nil"
dereference pointer = do
(Heap heap' _) <- gets runtimeHeap
let maybeAtom = Map.lookup pointer heap'
case maybeAtom of
Nothing -> error "Dangling pointer."
(Just atom) -> return atom
-- | A function with this type can be used as a scheme primitive function
-- by apply.
type SchemePrimitive = HeapPointer -> SchemeMonad HeapPointer
-- | Allocate a cons on the heap
primitiveCons :: HeapPointer -> HeapPointer -> SchemeMonad HeapPointer
primitiveCons carp cdrp = do
allocSchemeAtomic $ SchemeCons carp cdrp
cons :: SchemePrimitive
cons argp = do
carp <- car argp
cdrp <- (\argp -> car argp >>= cdr) argp
primitiveCons carp cdrp
car :: SchemePrimitive
car e = do
c <- dereference e
case c of
(SchemeCons p _) -> return p
otherwise -> do
heap <- gets runtimeHeap
error $ "Car applied to non-cons: " ++ (show c) ++ "\n" ++ show heap
cdr :: SchemePrimitive
cdr e = do
c <- dereference e
case c of
(SchemeCons _ p) -> return p
otherwise -> error $ "Cdr applied to non-cons: " ++ (show c)
-- | Construct a closure.
lambda :: SchemePrimitive
lambda argp = do
formalArguments <- car argp
functionBody <- cdr argp
env <- gets environment
primitiveCons formalArguments functionBody >>= primitiveCons env
-- | Evaluate a list of statements and return the result of the last one.
begin :: SchemePrimitive
begin argp = do
r <- car argp >>= eval
n <- cdr argp
case n of
SchemeNil -> return r
otherwise -> begin n
-- | Perform function application.
apply :: SchemePrimitive
apply argp = do
funp <- car argp
fun <- dereference funp
case fun of
(SchemeSymbol "nil") -> error "Attempted to apply NIL."
(SchemeSymbol "lamba") -> lambda funp
--(SchemeSymbol "define") -> define cdr'
otherwise -> do
(closure:args) <- evalList funp
formals <- car closure
closureEnvironment <- cdr closure >>= car
closureBody <- cdr closure >>= cdr >>= car
s <- get
env <- gets environment
put $ s { environment = closureEnvironment,
continuation = env }
bindFormals formals args
r <- begin closureBody
s <- get
envn <- gets continuation
put $ s { environment = envn }
return r
where evalList :: HeapPointer -> SchemeMonad [HeapPointer]
evalList argp = do
e <- car argp
r <- eval e
n <- cdr argp
case n of
SchemeNil -> return []
otherwise -> do
rn <- evalList n
return $ r : rn
bindFormals :: HeapPointer -> [HeapPointer] -> SchemeMonad ()
bindFormals SchemeNil [] = return ()
bindFormals SchemeNil _ = error "Too many arguments to function."
bindFormals _ [] = error "Too few arguments to function."
bindFormals formalsp (arg:argpv) = do
argp <- car formalsp
primitiveDefine argp arg
nf <- cdr formalsp
bindFormals nf argpv
-- | Primitive define.
primitiveDefine :: HeapPointer -> HeapPointer -> SchemeMonad ()
primitiveDefine symbolp valp = do
env <- gets environment
nc <- primitiveCons symbolp valp
nenv <- primitiveCons nc env
s <- get
put $ s { environment = nenv}
return ()
-- | Define.
define :: SchemePrimitive
define argp = do
[symbolp, valp] <- builtinGetArgs 2 argp
primitiveDefine symbolp valp
return SchemeNil
-- | Looks up a value in an association list.
assoc :: SchemePrimitive
assoc argp = do
[kp, vlp] <- builtinGetArgs 2 argp
assoc' kp vlp
where assoc' :: HeapPointer -> HeapPointer -> SchemeMonad HeapPointer
assoc' kp vlp = do
valp <- car vlp
consp <- cdr vlp
k <- car valp
kpv <- dereference kp
kv <- dereference k
if kpv == kv
then cdr valp
else do
if consp == SchemeNil
then return SchemeNil
else assoc' kp consp
resolve :: SchemePrimitive
resolve argp = do
env <- gets environment
res <- assoc env
case res of
SchemeNil -> error $ "Unbound symbol: " ++ (show res)
otherwise -> return res
builtinGetArgs :: Integer -> HeapPointer -> SchemeMonad [HeapPointer]
builtinGetArgs 0 SchemeNil = return []
builtinGetArgs 0 p = error $ "Too many arguments to function." ++ (show p)
builtinGetArgs n SchemeNil = error $ "Too few arguments to function. " ++ (show n)
builtinGetArgs num argp = do
arg <- car argp
nxt <- cdr argp
nval <- builtinGetArgs (num - 1) nxt
return $ arg : nval
eval :: SchemePrimitive
eval SchemeNil = return $ SchemeNil
eval a = do
s <- dereference a
case s of
(SchemeSymbol _) -> resolve a
(SchemeInteger _) -> return a
(SchemeCons _ _) -> apply a
read' :: String -> SchemeMonad HeapPointer
read' str = do
h <- gets runtimeHeap
let r = runParser schemeTopLevelParser h "" str
case r of
(Right (newHeap, e)) -> do
s <- get
put $ s { runtimeHeap = newHeap }
return e
(Left e) -> error $ "Parser error: " ++ (show e)
example str = do
let pointer = HeapPointer 0
(Right (heap, heapPointer)) = runParser schemeTopLevelParser (Heap (fromList []) (HeapPointer 0)) "" str
runState (eval heapPointer) $ SchemeEnvironment heap SchemeNil SchemeNil