scm.hs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077
  1. {-
  2. interpretive-scheme --- Scheme interpreter inspired by a Haskell joke
  3. Copyright © 2018 Alex Vong <alexvong1995@gmail.com>
  4. This file is part of interpretive-scheme.
  5. interpretive-scheme is free software; you can redistribute it and/or modify it
  6. under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 3 of the License, or (at
  8. your option) any later version.
  9. interpretive-scheme is distributed in the hope that it will be useful, but
  10. WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with interpretive-scheme. If not, see <http://www.gnu.org/licenses/>.
  15. -}
  16. module Main where
  17. import Prelude hiding (error,
  18. exp,
  19. fail,
  20. getLine,
  21. null,
  22. pred,
  23. print,
  24. putStr,
  25. putStrLn,
  26. read,
  27. readFile,
  28. reverse,
  29. seq)
  30. import qualified Prelude as P (getLine, putStr, putStrLn, readFile)
  31. import Data.Functor (($>))
  32. import qualified Control.Applicative as CA (liftA2)
  33. import Control.Monad (join)
  34. import qualified System.IO as SI (isEOF)
  35. import System.Directory (Permissions, readable)
  36. import System.Directory as SD (doesFileExist, getPermissions)
  37. import qualified Data.IORef as DI
  38. import Text.Parsec hiding (Error, ParseError, newline, noneOf, oneOf)
  39. import qualified Text.Parsec as TP (ParseError, noneOf, oneOf)
  40. import Text.Parsec.String
  41. type IO'Ref = DI.IORef
  42. type Parse'Error = TP.ParseError
  43. data SCM = Bool Bool
  44. | Integer Integer
  45. | String String
  46. | Identifier String
  47. | Nil
  48. | Pair SCM SCM
  49. | Primitive'Procedure String (SCM -> SCM -> IO SCM)
  50. | Compound'Procedure SCM SCM SCM
  51. | End'of'File
  52. | Binding SCM (IO'Ref SCM)
  53. | Empty'Frame
  54. | Frame SCM SCM
  55. | Empty'Environment
  56. | Environment (IO'Ref SCM) SCM
  57. | Error String
  58. put'str :: String -> IO ()
  59. put'str = P.putStr
  60. put'str'ln :: String -> IO ()
  61. put'str'ln = P.putStrLn
  62. get'line :: IO String
  63. get'line = P.getLine
  64. read'file :: String -> IO String
  65. read'file = P.readFile
  66. is'eof :: IO Bool
  67. is'eof = SI.isEOF
  68. does'file'exist :: String -> IO Bool
  69. does'file'exist = SD.doesFileExist
  70. get'permissions :: String -> IO Permissions
  71. get'permissions = SD.getPermissions
  72. new'io'ref :: a -> IO (IO'Ref a)
  73. new'io'ref = DI.newIORef
  74. read'io'ref :: IO'Ref a -> IO a
  75. read'io'ref = DI.readIORef
  76. write'io'ref :: IO'Ref a -> a -> IO ()
  77. write'io'ref = DI.writeIORef
  78. modify'io'ref ::IO'Ref a -> (a -> a) -> IO ()
  79. modify'io'ref = DI.modifyIORef
  80. lift'a2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
  81. lift'a2 = CA.liftA2
  82. none'of :: String -> Parser Char
  83. none'of = TP.noneOf
  84. one'of :: String -> Parser Char
  85. one'of = TP.oneOf
  86. (<:>) :: Applicative f => f a -> f [a] -> f [a]
  87. (<:>) = lift'a2 (:)
  88. (.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
  89. (.:) = (.).(.)
  90. (..:) :: (d -> e) -> (a -> b -> c -> d) -> a -> b -> c -> e
  91. (..:) = (.).(.).(.)
  92. put'ln :: IO ()
  93. put'ln = put'str'ln ""
  94. put'ln'str'ln :: String -> IO ()
  95. put'ln'str'ln str = put'ln *> put'str'ln str
  96. is'file'readable :: String -> IO Bool
  97. is'file'readable str = readable <$> get'permissions str
  98. skip'spaces :: Parser ()
  99. skip'spaces = spaces
  100. skip'eof :: Parser ()
  101. skip'eof = skip'spaces *> eof
  102. read'bool :: Parser SCM
  103. read'bool = char '#' *>
  104. ((char 't' $> Bool True) <|>
  105. (char 'f' $> Bool False))
  106. read'0'to'9 :: Parser Integer
  107. read'0'to'9 = (char '0' $> 0) <|>
  108. (char '1' $> 1) <|>
  109. (char '2' $> 2) <|>
  110. (char '3' $> 3) <|>
  111. (char '4' $> 4) <|>
  112. (char '5' $> 5) <|>
  113. (char '6' $> 6) <|>
  114. (char '7' $> 7) <|>
  115. (char '8' $> 8) <|>
  116. (char '9' $> 9)
  117. read'integer'aps :: Integer -> Parser Integer
  118. read'integer'aps accum = (((+ (accum * 10)) <$> read'0'to'9) >>=
  119. read'integer'aps) <|>
  120. pure accum
  121. read'integer :: Parser SCM
  122. read'integer = read'0'to'9 >>= fmap Integer . read'integer'aps
  123. read'escape'sequence :: Parser Char
  124. read'escape'sequence = char '\\' *>
  125. (char '"' <|> char '\\')
  126. read'string :: Parser SCM
  127. read'string = char '"' *>
  128. (String <$> many (none'of "\"\\" <|> read'escape'sequence)) <*
  129. char '"'
  130. read'letter :: Parser Char
  131. read'letter = letter
  132. read'special'initial :: Parser Char
  133. read'special'initial = one'of "!$%&*/:<=>?^_~"
  134. read'initial :: Parser Char
  135. read'initial = read'letter <|> read'special'initial
  136. read'digit :: Parser Char
  137. read'digit = digit
  138. read'special'subsequent :: Parser Char
  139. read'special'subsequent = one'of "+-.@"
  140. read'subsequent :: Parser Char
  141. read'subsequent = read'initial <|> read'digit <|> read'special'subsequent
  142. read'peculiar'identifier :: Parser String
  143. read'peculiar'identifier = string "+" <|> string "-" <|> string "..."
  144. read'identifier :: Parser SCM
  145. read'identifier = Identifier <$>
  146. ((read'initial <:> many read'subsequent) <|>
  147. read'peculiar'identifier)
  148. read'atom :: Parser SCM
  149. read'atom = read'bool <|> read'integer <|> read'string <|> read'identifier
  150. read'list'aps :: SCM -> Parser SCM
  151. read'list'aps accum = skip'spaces >>
  152. ((char ')' $> accum) <|>
  153. ((Pair <$> read'scm <*> pure accum) >>= read'list'aps))
  154. read'list :: Parser SCM
  155. read'list = read'list'aps Nil
  156. make'quote :: SCM -> SCM
  157. make'quote text'of'quotation = Pair (Identifier "quote") text'of'quotation
  158. read'quoted'scm :: Parser SCM
  159. read'quoted'scm = string "'" *>
  160. (make'quote <$> (Pair <$> read'scm <*> pure Nil))
  161. lookup'first'error :: SCM -> Maybe SCM
  162. lookup'first'error args =
  163. case args of
  164. Nil -> Nothing
  165. Error _ -> Just $ args
  166. Pair (Error str) _ -> Just $ Error str
  167. Pair _ rest -> lookup'first'error rest
  168. _ -> Just $ Error $ "Not a list: LOOKUP-FIRST-ERROR " ++ print'scm args
  169. error :: String -> SCM -> SCM
  170. error str args =
  171. case args of
  172. Nil -> Error str
  173. Error _ -> args
  174. Pair _ _ ->
  175. case lookup'first'error args of
  176. Nothing -> Error $ print'pair'aps str args
  177. Just scm -> case scm of
  178. Error _ -> scm
  179. _ -> Error $ "Not an error: ERROR " ++ print'scm args
  180. _ -> Error $ "Not a list: ERROR " ++ print'scm args
  181. fail :: String -> SCM -> SCM
  182. fail str val = error str (Pair val Nil)
  183. reverse'aps :: SCM -> SCM -> SCM
  184. reverse'aps accum ls = case ls of
  185. Nil -> accum
  186. Pair first rest -> reverse'aps (Pair first accum) rest
  187. _ -> fail "Not a list: REVERSE-APS" ls
  188. reverse :: SCM -> SCM
  189. reverse = reverse'aps Nil
  190. read'scm :: Parser SCM
  191. read'scm = skip'spaces *>
  192. (read'atom <|>
  193. read'quoted'scm <|>
  194. (char '(' *> (reverse <$> read'list)))
  195. read'from'string :: String -> SCM
  196. read'from'string str = case parse (read'scm <* skip'eof) "scm" str of
  197. Left err -> Error $ show err
  198. Right val -> val
  199. wrap'begin :: String -> String
  200. wrap'begin str = "(begin" ++ str ++ ")"
  201. read'from'file :: SCM -> IO SCM
  202. read'from'file scm =
  203. case scm of
  204. String str ->
  205. does'file'exist str >>=
  206. \pred -> if pred
  207. then is'file'readable str >>=
  208. \pred' ->
  209. if pred'
  210. then read'from'string . wrap'begin <$> read'file str
  211. else pure $ fail "Not readable: READ-FROM-FILE" scm
  212. else pure $ fail "Not a file: READ-FROM-FILE" scm
  213. _ -> pure $ fail "Not a string: READ-FROM-FILE" scm
  214. read :: IO SCM
  215. read = is'eof >>=
  216. \pred -> if pred
  217. then pure End'of'File
  218. else read'from'string <$> get'line
  219. list'of'values'aps :: SCM -> SCM -> SCM -> SCM -> IO SCM
  220. list'of'values'aps exps global'env env accum =
  221. case exps of
  222. Nil -> pure $ accum
  223. Pair first rest -> (Pair <$> eval first global'env env <*> pure accum) >>=
  224. list'of'values'aps rest global'env env
  225. _ -> pure $ fail "Not a list: LIST-OF-VALUES-APS" exps
  226. list'of'values :: SCM -> SCM -> SCM -> IO SCM
  227. list'of'values exps global'env env =
  228. reverse <$> list'of'values'aps exps global'env env Nil
  229. scan'variable :: SCM -> SCM -> SCM -> Maybe SCM
  230. scan'variable var val frame =
  231. case frame of
  232. Empty'Frame -> Nothing
  233. Frame (Binding (Identifier str) val') first'enclosing'frame ->
  234. case var of
  235. Identifier str' -> if str == str'
  236. then Just $ Binding (Identifier str) val'
  237. else scan'variable var val first'enclosing'frame
  238. _ -> Just $ fail "Not an identifier: SCAN-VARIABLE" var
  239. _ -> Just $ fail "Not a frame: SCAN-VARIABLE" frame
  240. add'binding'to'first'frame :: SCM -> SCM -> SCM -> IO SCM
  241. add'binding'to'first'frame var val env =
  242. case env of
  243. Environment first'frame _ ->
  244. ((Frame <$>
  245. (Binding <$> pure var <*> new'io'ref val) <*>
  246. read'io'ref first'frame) >>=
  247. write'io'ref first'frame) $>
  248. Nil
  249. Empty'Environment ->
  250. pure $ fail "Empty environment: ADD-BINDING-TO-FIRST-FRAME" env
  251. _ -> pure $ fail "Not an environment: ADD-BINDING-TO-FIRST-FRAME" env
  252. define'variable :: SCM -> SCM -> SCM -> IO SCM
  253. define'variable var val env =
  254. case env of
  255. Environment first'frame _ ->
  256. read'io'ref first'frame >>=
  257. \frame -> case scan'variable var val frame of
  258. Nothing -> add'binding'to'first'frame var val env
  259. Just (Binding _ val') -> write'io'ref val' val $> Nil
  260. Just scm -> pure $ fail "Not a binding: DEFINE-VARIABLE" scm
  261. Empty'Environment ->
  262. pure $ fail "Empty environment: DEFINE-VARIABLE" env
  263. _ -> pure $ fail "Not an environment: DEFINE-VARIABLE" env
  264. eval :: SCM -> SCM -> SCM -> IO SCM
  265. eval exp global'env env =
  266. case env of
  267. Environment _ _ ->
  268. case exp of
  269. Bool _ -> pure exp
  270. Integer _ -> pure exp
  271. String _ -> pure exp
  272. Identifier _ -> lookup'variable'value exp env
  273. Pair (Identifier "quote") (Pair text'of'quotation Nil) ->
  274. pure text'of'quotation
  275. Pair (Identifier "define") definition ->
  276. eval'definition
  277. (definition'variable definition)
  278. (definition'value definition)
  279. global'env
  280. env
  281. (Pair
  282. (Identifier "if")
  283. (Pair predicate (Pair consequent (Pair alternative Nil)))) ->
  284. eval'if predicate consequent alternative global'env env
  285. Pair (Identifier "lambda") (Pair parameters body) ->
  286. pure $ Compound'Procedure parameters body env
  287. Pair (Identifier "begin") actions ->
  288. eval'sequence actions global'env env
  289. Pair (Identifier "cond") clauses ->
  290. eval (cond'to'if clauses) global'env env
  291. Pair (Identifier "and") clauses -> eval'and clauses global'env env
  292. Pair (Identifier "or") clauses -> eval'or clauses global'env env
  293. Pair (Identifier "let") (Pair bindings body) ->
  294. eval (let'to'combination bindings body) global'env env
  295. Pair (Identifier "let*") (Pair bindings body) ->
  296. eval (let'star'to'nested'lets bindings body) global'env env
  297. Pair operator operands ->
  298. join (apply <$>
  299. eval operator global'env env <*>
  300. list'of'values operands global'env env <*>
  301. pure global'env)
  302. _ -> pure $ fail "Unknown expression type: EVAL" exp
  303. _ -> pure $ fail "Not an environment: EVAL" env
  304. definition'variable :: SCM -> SCM
  305. definition'variable exp =
  306. case exp of
  307. Pair (Identifier str) _ -> Identifier str
  308. Pair (Pair first _) _ -> first
  309. _ -> fail "Unknown expression type: DEFINITION-VARIABLE" exp
  310. make'lambda :: SCM -> SCM -> SCM
  311. make'lambda parameters body =
  312. Pair (Identifier "lambda") (Pair parameters body)
  313. definition'value :: SCM -> SCM
  314. definition'value exp =
  315. case exp of
  316. Pair (Identifier _) (Pair value Nil) -> value
  317. Pair (Pair _ parameters) body -> make'lambda parameters body
  318. _ -> fail "Unknown expression type: DEFINITION-VALUE" exp
  319. eval'definition :: SCM -> SCM -> SCM -> SCM -> IO SCM
  320. eval'definition variable value global'env env =
  321. eval value global'env env >>=
  322. \scm -> case scm of
  323. Error _ -> pure scm
  324. _ -> define'variable variable scm env
  325. eval'if :: SCM -> SCM -> SCM -> SCM -> SCM -> IO SCM
  326. eval'if predicate consequent alternative global'env env =
  327. eval predicate global'env env >>=
  328. \pred -> case pred of
  329. Error _ -> pure pred
  330. Bool False -> eval alternative global'env env
  331. _ -> eval consequent global'env env
  332. eval'sequence :: SCM -> SCM -> SCM -> IO SCM
  333. eval'sequence exps global'env env =
  334. case exps of
  335. Nil -> pure Nil
  336. Pair exp Nil -> eval exp global'env env
  337. Pair first'exp rest'exps ->
  338. eval first'exp global'env env >>=
  339. \scm -> case scm of
  340. Error str -> pure $ Error str
  341. _ -> eval'sequence rest'exps global'env env
  342. _ -> pure $ fail "Not a list: EVAL-SEQUENCE" exps
  343. make'if :: SCM -> SCM -> SCM -> SCM
  344. make'if predicate consequent alternative =
  345. Pair
  346. (Identifier "if")
  347. (Pair predicate (Pair consequent (Pair alternative Nil)))
  348. make'begin :: SCM -> SCM
  349. make'begin seq = Pair (Identifier "begin") seq
  350. cond'to'if :: SCM -> SCM
  351. cond'to'if exp =
  352. case exp of
  353. Nil -> Pair (Identifier "quote") (Pair Nil Nil)
  354. Pair (Pair (Identifier "else") actions) Nil -> make'begin actions
  355. Pair (Pair (Identifier "else") _) _ ->
  356. fail "ELSE clause isn't last: COND->IF" exp
  357. Pair (Pair predicate actions) rest ->
  358. make'if predicate (make'begin actions) (cond'to'if rest)
  359. _ -> fail "Not a list: COND->IF" exp
  360. eval'and :: SCM -> SCM -> SCM -> IO SCM
  361. eval'and clauses global'env env =
  362. case clauses of
  363. Nil -> pure $ Bool True
  364. Pair scm Nil -> eval scm global'env env
  365. Pair first rest -> eval first global'env env >>=
  366. \pred -> case pred of
  367. Error _ -> pure pred
  368. Bool False -> pure $ Bool False
  369. _ -> eval'and rest global'env env
  370. _ -> pure $ fail "Not a list: EVAL-AND" clauses
  371. eval'or :: SCM -> SCM -> SCM -> IO SCM
  372. eval'or clauses global'env env =
  373. case clauses of
  374. Nil -> pure $ Bool False
  375. Pair scm Nil -> eval scm global'env env
  376. Pair first rest -> eval first global'env env >>=
  377. \pred -> case pred of
  378. Bool False -> eval'or rest global'env env
  379. _ -> pure pred
  380. _ -> pure $ fail "Not a list: EVAL-OR" clauses
  381. map'first'aps :: SCM -> SCM -> SCM
  382. map'first'aps accum ls =
  383. case ls of
  384. Nil -> accum
  385. Pair (Pair first _) rest -> map'first'aps (Pair first accum) rest
  386. Pair scm _ -> fail "Not a list with length >= 1: MAP-FIRST-APS" scm
  387. _ -> fail "Not a list: MAP-FIRST-APS" ls
  388. map'first :: SCM -> SCM
  389. map'first = reverse . map'first'aps Nil
  390. map'second'aps :: SCM -> SCM -> SCM
  391. map'second'aps accum ls =
  392. case ls of
  393. Nil -> accum
  394. Pair (Pair _ (Pair second _)) rest ->
  395. map'second'aps (Pair second accum) rest
  396. Pair scm _ -> fail "Not a list with length >= 2: MAP-SECOND-APS" scm
  397. _ -> fail "Not a list: MAP-SECOND-APS" ls
  398. map'second :: SCM -> SCM
  399. map'second = reverse . map'second'aps Nil
  400. let'to'combination :: SCM -> SCM -> SCM
  401. let'to'combination bindings body =
  402. Pair (make'lambda (map'first bindings) body) (map'second bindings)
  403. make'let :: SCM -> SCM -> SCM
  404. make'let bindings body = Pair (Identifier "let") (Pair bindings body)
  405. let'star'to'nested'lets :: SCM -> SCM -> SCM
  406. let'star'to'nested'lets bindings body =
  407. case bindings of
  408. Nil -> make'let Nil body
  409. Pair first rest ->
  410. make'let (Pair first Nil) (Pair (let'star'to'nested'lets rest body) Nil)
  411. _ -> fail "Not a list: LET*->NESTED-LETS" bindings
  412. make'frame'aps :: SCM -> SCM -> SCM -> IO SCM
  413. make'frame'aps vars vals accum =
  414. case vars of
  415. Nil ->
  416. case vals of
  417. Nil -> pure accum
  418. Pair _ _ ->
  419. pure $ fail "Too many arguments supplied: MAKE-FRAME-APS" vals
  420. _ -> pure $ fail "Not a list: MAKE-FRAME-APS" vals
  421. Pair first'var rest'vars ->
  422. case vals of
  423. Pair first'val rest'vals ->
  424. (Frame <$>
  425. (Binding <$> pure first'var <*> new'io'ref first'val) <*>
  426. pure accum) >>=
  427. make'frame'aps rest'vars rest'vals
  428. Nil -> pure $ fail "Too few arguments supplied: MAKE-FRAME-APS" vals
  429. _ -> pure $ fail "Not a list: MAKE-FRAME-APS" vals
  430. _ -> pure $ fail "Not a list: MAKE-FRAME-APS" vars
  431. make'frame :: SCM -> SCM -> IO SCM
  432. make'frame vars vals = make'frame'aps vars vals Empty'Frame
  433. extend'environment :: SCM -> SCM -> SCM -> IO SCM
  434. extend'environment vars vals base'env =
  435. make'frame vars vals >>=
  436. \frame -> case frame of
  437. Error _ -> pure frame
  438. _ -> Environment <$> new'io'ref frame <*> pure base'env
  439. lookup'binding'variable'value :: SCM -> SCM -> Maybe (IO SCM)
  440. lookup'binding'variable'value var binding =
  441. case binding of
  442. Binding (Identifier str) val ->
  443. case var of
  444. Identifier str' -> if str == str'
  445. then Just $ read'io'ref val
  446. else Nothing
  447. _ -> Just $
  448. pure $
  449. fail "Not an identifier: LOOKUP-BINDING-VARIABLE-VALUE" var
  450. _ -> Just $
  451. pure $
  452. fail "Not a binding: LOOKUP-BINDING-VARIABLE-VALUE" binding
  453. lookup'frame'variable'value :: SCM -> SCM -> Maybe (IO SCM)
  454. lookup'frame'variable'value var frame =
  455. case frame of
  456. Empty'Frame -> Nothing
  457. Frame first'binding enclosing'frame ->
  458. case lookup'binding'variable'value var first'binding of
  459. Nothing -> lookup'frame'variable'value var enclosing'frame
  460. scm -> scm
  461. _ -> Just $ pure $ fail "Not a frame: LOOKUP-FRAME-VARIABLE-VALUE" frame
  462. lookup'variable'value :: SCM -> SCM -> IO SCM
  463. lookup'variable'value var env =
  464. case env of
  465. Empty'Environment ->
  466. pure $ fail "Unbound variable: LOOKUP-VARIABLE-VALUE" var
  467. Environment first'frame enclosing'environment ->
  468. read'io'ref first'frame >>=
  469. \frame -> case lookup'frame'variable'value var frame of
  470. Nothing -> lookup'variable'value var enclosing'environment
  471. Just scm -> scm
  472. _ -> pure $ fail "Not an environment: LOOKUP-VARIABLE-VALUE" env
  473. apply :: SCM -> SCM -> SCM -> IO SCM
  474. apply proc args global'env =
  475. case lookup'first'error args of
  476. Nothing -> case proc of
  477. Primitive'Procedure _ primitive'implementation ->
  478. primitive'implementation args global'env
  479. Compound'Procedure parameters body env ->
  480. apply'compound'procedure parameters body args global'env env
  481. _ -> pure $ fail "Unknown procedure type: APPLY" proc
  482. Just scm -> case scm of
  483. Error _ -> pure scm
  484. _ -> pure $ fail "Not an error: APPLY" scm
  485. bind'parameters :: SCM -> SCM -> SCM -> IO SCM
  486. bind'parameters parameters args env =
  487. case parameters of
  488. Identifier _ ->
  489. extend'environment (Pair parameters Nil) (Pair args Nil) env
  490. Pair _ _ -> extend'environment parameters args env
  491. Nil -> extend'environment parameters args env
  492. _ -> pure $ fail "Unknown parameter type: BIND-PARAMETERS" parameters
  493. apply'compound'procedure :: SCM -> SCM -> SCM -> SCM -> SCM -> IO SCM
  494. apply'compound'procedure parameters body args global'env env =
  495. bind'parameters parameters args env >>=
  496. \env' -> case env' of
  497. Error _ -> pure env'
  498. _ -> eval'sequence body global'env env'
  499. plus :: SCM -> SCM -> SCM
  500. plus a b = case a of
  501. Integer a' -> case b of
  502. Integer b' -> Integer $ a' + b'
  503. _ -> fail "Not an integer: +" b
  504. _ -> fail "Not an integer: +" a
  505. minus :: SCM -> SCM -> SCM
  506. minus a b = case a of
  507. Integer a' -> case b of
  508. Integer b' -> Integer $ a' - b'
  509. _ -> fail "Not an integer: -" b
  510. _ -> fail "Not an integer: -" a
  511. times :: SCM -> SCM -> SCM
  512. times a b = case a of
  513. Integer a' -> case b of
  514. Integer b' -> Integer $ a' * b'
  515. _ -> fail "Not an integer: *" b
  516. _ -> fail "Not an integer: *" a
  517. quotient :: SCM -> SCM -> SCM
  518. quotient a b =
  519. case a of
  520. Integer a' -> case b of
  521. Integer 0 -> fail "Division by zero: QUOTIENT" b
  522. Integer b' -> Integer $ quot a' b'
  523. _ -> fail "Not an integer: QUOTIENT" b
  524. _ -> fail "Not an integer: QUOTIENT" a
  525. remainder :: SCM -> SCM -> SCM
  526. remainder a b =
  527. case a of
  528. Integer a' -> case b of
  529. Integer 0 -> fail "Division by zero: REMAINDER" b
  530. Integer b' -> Integer $ rem a' b'
  531. _ -> fail "Not an integer: REMAINDER" b
  532. _ -> fail "Not an integer: REMAINDER" a
  533. is'integer :: SCM -> SCM
  534. is'integer scm = case scm of
  535. Integer _ -> Bool True
  536. _ -> Bool False
  537. integer'equal :: SCM -> SCM -> SCM
  538. integer'equal a b = case a of
  539. Integer a' -> case b of
  540. Integer b' -> Bool $ a' == b'
  541. _ -> fail "Not an integer: =" b
  542. _ -> fail "Not an integer: =" a
  543. less :: SCM -> SCM -> SCM
  544. less a b = case a of
  545. Integer a' -> case b of
  546. Integer b' -> Bool $ a' < b'
  547. _ -> fail "Not an integer: <" b
  548. _ -> fail "Not an integer: <" a
  549. greater :: SCM -> SCM -> SCM
  550. greater a b = case a of
  551. Integer a' -> case b of
  552. Integer b' -> Bool $ a' > b'
  553. _ -> fail "Not an integer: >" b
  554. _ -> fail "Not an integer: >" a
  555. less'or'equal :: SCM -> SCM -> SCM
  556. less'or'equal a b = case a of
  557. Integer a' -> case b of
  558. Integer b' -> Bool $ a' <= b'
  559. _ -> fail "Not an integer: <=" b
  560. _ -> fail "Not an integer: <=" a
  561. greater'or'equal :: SCM -> SCM -> SCM
  562. greater'or'equal a b = case a of
  563. Integer a' -> case b of
  564. Integer b' -> Bool $ a' >= b'
  565. _ -> fail "Not an integer: >=" b
  566. _ -> fail "Not an integer: >=" a
  567. string'is'equal :: SCM -> SCM -> SCM
  568. string'is'equal a b = case a of
  569. String a' -> case b of
  570. String b' -> Bool $ a' == b'
  571. _ -> fail "Not a string: STRING=?" b
  572. _ -> fail "Not a string: STRING=?" a
  573. string'append :: SCM -> SCM -> SCM
  574. string'append a b = case a of
  575. String a' -> case b of
  576. String b' -> String $ a' ++ b'
  577. _ -> fail "Not a string: STRING-APPEND" b
  578. _ -> fail "Not a string: STRING-APPEND" a
  579. string'length'aps :: Integer -> String -> Integer
  580. string'length'aps accum str = case str of
  581. "" -> accum
  582. _:str' -> string'length'aps (accum + 1) str'
  583. string'length :: SCM -> SCM
  584. string'length str = case str of
  585. String str' -> Integer $ string'length'aps 0 str'
  586. _ -> fail "Not a string: STRING-APPEND" str
  587. string'drop :: SCM -> SCM -> SCM
  588. string'drop str start =
  589. case str of
  590. String str' ->
  591. case start of
  592. Integer start' ->
  593. if start' == 0
  594. then str
  595. else if start' > 0
  596. then case str' of
  597. _:str'' ->
  598. string'drop (String str'') (Integer (start' - 1))
  599. "" -> fail "String too short: STRING-DROP" str
  600. else fail "Negative index: STRING-DROP" start
  601. _ -> fail "Not an integer: STRING-DROP" start
  602. _ -> fail "Not a string: STRING-DROP" str
  603. string'reverse'aps :: String -> String -> String
  604. string'reverse'aps accum str' = case str' of
  605. "" -> accum
  606. chr:str -> string'reverse'aps (chr:accum) str
  607. string'reverse :: SCM -> SCM
  608. string'reverse str = case str of
  609. String str' -> String $ string'reverse'aps "" str'
  610. _ -> fail "Not a string: STRING-REVERSE" str
  611. string'drop'right :: SCM -> SCM -> SCM
  612. string'drop'right str end =
  613. string'reverse (string'drop (string'reverse str) end)
  614. substring :: SCM -> SCM -> SCM -> SCM
  615. substring str start end =
  616. string'drop'right (string'drop str start) (minus (string'length str) end)
  617. is'string :: SCM -> SCM
  618. is'string scm = case scm of
  619. String _ -> Bool True
  620. _ -> Bool False
  621. string'to'symbol :: SCM -> SCM
  622. string'to'symbol str = case str of
  623. String str' -> Identifier str'
  624. _ -> fail "Not a string: STRING->SYMBOL" str
  625. symbol'to'string :: SCM -> SCM
  626. symbol'to'string sym = case sym of
  627. Identifier str -> String str
  628. _ -> fail "Not a symbol: SYMBOL->STRING" sym
  629. is'symbol :: SCM -> SCM
  630. is'symbol scm = case scm of
  631. Identifier _ -> Bool True
  632. _ -> Bool False
  633. car :: SCM -> SCM
  634. car scm = case scm of
  635. Pair first _ -> first
  636. _ -> fail "Not a pair: CAR" scm
  637. cdr :: SCM -> SCM
  638. cdr scm = case scm of
  639. Pair _ rest -> rest
  640. _ -> fail "Not a pair: CDR" scm
  641. cons :: SCM -> SCM -> SCM
  642. cons = Pair
  643. is'null :: SCM -> SCM
  644. is'null scm = case scm of
  645. Nil -> Bool True
  646. _ -> Bool False
  647. is'pair :: SCM -> SCM
  648. is'pair scm = case scm of
  649. Pair _ _ -> Bool True
  650. _ -> Bool False
  651. is'boolean :: SCM -> SCM
  652. is'boolean scm = case scm of
  653. Bool _ -> Bool True
  654. _ -> Bool False
  655. is'eof'object :: SCM -> SCM
  656. is'eof'object scm = case scm of
  657. End'of'File -> Bool True
  658. _ -> Bool False
  659. eof'object :: SCM
  660. eof'object = End'of'File
  661. is'primitive'procedure :: SCM -> SCM
  662. is'primitive'procedure scm = case scm of
  663. Primitive'Procedure _ _ -> Bool True
  664. _ -> Bool False
  665. is'compound'procedure :: SCM -> SCM
  666. is'compound'procedure scm = case scm of
  667. Compound'Procedure _ _ _ -> Bool True
  668. _ -> Bool False
  669. is'environment :: SCM -> SCM
  670. is'environment scm = case scm of
  671. Environment _ _ -> Bool True
  672. Empty'Environment -> Bool True
  673. _ -> Bool False
  674. error' :: SCM -> SCM -> SCM
  675. error' args' _ = case args' of
  676. Nil -> fail "Too few arguments supplied: ERROR" args'
  677. Pair scm args -> case scm of
  678. String str -> error str args
  679. _ -> fail "Not a string: ERROR" scm
  680. _ -> fail "Not a list: ERROR" args'
  681. const'nil :: SCM -> SCM
  682. const'nil scm = case scm of
  683. Error _ -> scm
  684. _ -> Nil
  685. load :: SCM -> SCM -> IO SCM
  686. load args global'env =
  687. case args of
  688. Pair scm Nil -> const'nil <$> join (eval <$>
  689. read'from'file scm <*>
  690. pure global'env <*>
  691. pure global'env)
  692. Pair _ _ -> pure $ fail "Too many arguments supplied: LOAD" args
  693. Nil -> pure $ fail "Too few arguments supplied: LOAD" args
  694. _ -> pure $ fail "Not a list: LOAD" args
  695. interaction'environment :: SCM -> SCM -> IO SCM
  696. interaction'environment args global'env =
  697. case args of
  698. Nil -> pure global'env
  699. Pair _ _ ->
  700. pure $ fail "Too many arguments supplied: INTERACTION-ENVIRONMENT" args
  701. _ -> pure $ fail "Not a list: INTERACTION-ENVIRONMENT" args
  702. eval' :: SCM -> SCM -> IO SCM
  703. eval' args global'env =
  704. case args of
  705. Pair exp (Pair env Nil) -> eval exp global'env env
  706. Pair _ _ -> pure $ fail "Too many/few arguments supplied: EVAL" args
  707. Nil -> pure $ fail "Too few arguments supplied: EVAL" args
  708. _ -> pure $ fail "Not a list: EVAL" args
  709. apply' :: SCM -> SCM -> IO SCM
  710. apply' args' global'env =
  711. case args' of
  712. Pair proc (Pair args Nil) -> apply proc args global'env
  713. Pair _ _ -> pure $ fail "Too many/few arguments supplied: APPLY" args'
  714. Nil -> pure $ fail "Too few arguments supplied: APPLY" args'
  715. _ -> pure $ fail "Not a list: APPLY" args'
  716. write :: SCM -> IO SCM
  717. write scm = put'str (print'scm scm) $> Nil
  718. display :: SCM -> IO SCM
  719. display scm = case scm of
  720. String str -> put'str str $> Nil
  721. _ -> put'str (print'scm scm) $> Nil
  722. newline :: IO SCM
  723. newline = put'ln $> Nil
  724. apply'nullary'operator :: (IO SCM) -> SCM -> SCM -> IO SCM
  725. apply'nullary'operator proc args _ =
  726. case args of
  727. Nil -> proc
  728. Pair _ _ ->
  729. pure $ fail "Too many arguments supplied: APPLY-NULLARY-OPERATOR" args
  730. _ -> pure $ fail "Not a list: APPLY-NULLARY-OPERATOR" args
  731. apply'unary'operator :: (SCM -> IO SCM) -> SCM -> SCM -> IO SCM
  732. apply'unary'operator proc args _ =
  733. case args of
  734. Pair scm Nil -> proc scm
  735. Pair _ _ ->
  736. pure $ fail "Too many arguments supplied: APPLY-UNARY-OPERATOR" args
  737. Nil -> pure $ fail "Too few arguments supplied: APPLY-UNARY-OPERATOR" args
  738. _ -> pure $ fail "Not a list: APPLY-UNARY-OPERATOR" args
  739. apply'binary'operator :: (SCM -> SCM -> IO SCM) -> SCM -> SCM -> IO SCM
  740. apply'binary'operator proc args _ =
  741. case args of
  742. Pair a (Pair b Nil) -> proc a b
  743. Pair _ _ ->
  744. pure $ fail "Too many/few arguments supplied: APPLY-BINARY-OPERATOR" args
  745. Nil -> pure $ fail "Too few arguments supplied: APPLY-BINARY-OPERATOR" args
  746. _ -> pure $ fail "Not a list: APPLY-BINARY-OPERATOR" args
  747. apply'ternary'operator :: (SCM -> SCM -> SCM -> IO SCM) -> SCM -> SCM -> IO SCM
  748. apply'ternary'operator proc args _ =
  749. case args of
  750. Pair a (Pair b (Pair c Nil)) -> proc a b c
  751. Pair _ _ ->
  752. pure $
  753. fail "Too many/few arguments supplied: APPLY-TERNARY-OPERATOR" args
  754. Nil ->
  755. pure $ fail "Too few arguments supplied: APPLY-TERNARY-OPERATOR" args
  756. _ -> pure $ fail "Not a list: APPLY-TERNARY-OPERATOR" args
  757. primitive'procedures :: [(String, SCM -> SCM -> IO SCM)]
  758. primitive'procedures =
  759. [("+", apply'binary'operator (pure .: plus)),
  760. ("-", apply'binary'operator (pure .: minus)),
  761. ("*", apply'binary'operator (pure .: times)),
  762. ("quotient", apply'binary'operator (pure .: quotient)),
  763. ("remainder", apply'binary'operator (pure .: remainder)),
  764. ("integer?", apply'unary'operator (pure . is'integer)),
  765. ("=", apply'binary'operator (pure .: integer'equal)),
  766. ("<", apply'binary'operator (pure .: less)),
  767. (">", apply'binary'operator (pure .: greater)),
  768. ("<=", apply'binary'operator (pure .: less'or'equal)),
  769. (">=", apply'binary'operator (pure .: greater'or'equal)),
  770. ("string=?", apply'binary'operator (pure .: string'is'equal)),
  771. ("string-append", apply'binary'operator (pure .: string'append)),
  772. ("string-length", apply'unary'operator (pure . string'length)),
  773. ("substring", apply'ternary'operator (pure ..: substring)),
  774. ("string?", apply'unary'operator (pure . is'string)),
  775. ("string->symbol", apply'unary'operator (pure . string'to'symbol)),
  776. ("symbol->string", apply'unary'operator (pure . symbol'to'string)),
  777. ("symbol?", apply'unary'operator (pure . is'symbol)),
  778. ("car", apply'unary'operator (pure . car)),
  779. ("cdr", apply'unary'operator (pure . cdr)),
  780. ("cons", apply'binary'operator (pure .: cons)),
  781. ("null?", apply'unary'operator (pure . is'null)),
  782. ("pair?", apply'unary'operator (pure . is'pair)),
  783. ("boolean?", apply'unary'operator (pure . is'boolean)),
  784. ("eof-object?", apply'unary'operator (pure . is'eof'object)),
  785. ("eof-object", apply'nullary'operator (pure eof'object)),
  786. ("primitive-procedure?", apply'unary'operator
  787. (pure . is'primitive'procedure)),
  788. ("compound-procedure?", apply'unary'operator
  789. (pure . is'compound'procedure)),
  790. ("environment?", apply'unary'operator (pure . is'environment)),
  791. ("error", pure .: error'),
  792. ("read", apply'nullary'operator read),
  793. ("load", load),
  794. ("interaction-environment", interaction'environment),
  795. ("eval", eval'),
  796. ("apply", apply'),
  797. ("write", apply'unary'operator write),
  798. ("display", apply'unary'operator display),
  799. ("newline", apply'nullary'operator newline)]
  800. build'frame'aps :: [(String, SCM -> SCM -> IO SCM)] -> SCM -> IO SCM
  801. build'frame'aps alist accum =
  802. case alist of
  803. [] -> pure accum
  804. (name, proc):rest -> (Frame <$>
  805. (Binding <$>
  806. pure (Identifier name) <*>
  807. new'io'ref (Primitive'Procedure name proc)) <*>
  808. pure accum) >>=
  809. build'frame'aps rest
  810. build'frame :: [(String, SCM -> SCM -> IO SCM)] -> IO SCM
  811. build'frame alist = build'frame'aps alist Empty'Frame
  812. initial'env :: IO SCM
  813. initial'env = Environment <$>
  814. (build'frame primitive'procedures >>= new'io'ref) <*>
  815. pure Empty'Environment
  816. print'positive'aps :: String -> Integer -> String
  817. print'positive'aps accum n =
  818. case n of
  819. 0 -> "0" ++ accum
  820. 1 -> "1" ++ accum
  821. 2 -> "2" ++ accum
  822. 3 -> "3" ++ accum
  823. 4 -> "4" ++ accum
  824. 5 -> "5" ++ accum
  825. 6 -> "6" ++ accum
  826. 7 -> "7" ++ accum
  827. 8 -> "8" ++ accum
  828. 9 -> "9" ++ accum
  829. _ -> print'positive'aps (print'positive'aps accum (mod n 10)) (div n 10)
  830. print'positive :: Integer -> String
  831. print'positive = print'positive'aps ""
  832. print'negative :: Integer -> String
  833. print'negative n = "(- " ++ print'positive (- n) ++ ")"
  834. print'integer :: Integer -> String
  835. print'integer n
  836. | n > 0 = print'positive n
  837. | n < 0 = print'negative n
  838. | otherwise = "0"
  839. print'string'elements'aps :: String -> String -> String
  840. print'string'elements'aps accum str' = case str' of
  841. "" -> accum
  842. chr:str -> print'string'elements'aps
  843. (accum ++ case [chr] of
  844. "\"" -> "\\\""
  845. "\\" -> "\\\\"
  846. str'' -> str'')
  847. str
  848. print'string'elements :: String -> String
  849. print'string'elements = print'string'elements'aps ""
  850. print'string :: String -> String
  851. print'string str = "\"" ++ print'string'elements str ++ "\""
  852. print'pair'aps :: String -> SCM -> String
  853. print'pair'aps accum scm =
  854. case scm of
  855. Pair first rest -> print'pair'aps (accum ++ " " ++ print'scm first) rest
  856. Nil -> accum
  857. scm' -> accum ++ " . " ++ print'scm scm'
  858. print'scm :: SCM -> String
  859. print'scm scm =
  860. case scm of
  861. Bool True -> "#t"
  862. Bool False -> "#f"
  863. Integer n -> print'integer n
  864. String str -> print'string str
  865. Identifier str -> str
  866. Nil -> "()"
  867. Pair first rest -> print'pair'aps ("(" ++ print'scm first) rest ++ ")"
  868. Primitive'Procedure name _ -> "#[primitive-procedure " ++ name ++ "]"
  869. Compound'Procedure parameters _ _ ->
  870. "#[compound-procedure " ++ print'scm parameters ++ "]"
  871. End'of'File -> "#[the-end-of-file]"
  872. Binding _ _ -> "#[binding]"
  873. Empty'Frame -> "#[the-empty-frame]"
  874. Frame _ _ -> "#[frame]"
  875. Empty'Environment -> "#[the-empty-environment]"
  876. Environment _ _ -> "#[environment]"
  877. Error str -> str
  878. prepend'semicolons'aps :: String -> String -> String
  879. prepend'semicolons'aps accum str' = case str' of
  880. "" -> accum
  881. chr:str -> prepend'semicolons'aps
  882. (accum ++ case [chr] of
  883. "\n" -> "\n;;; "
  884. str'' -> str'')
  885. str
  886. prepend'semicolons :: String -> String
  887. prepend'semicolons = prepend'semicolons'aps ";;; "
  888. print :: SCM -> IO ()
  889. print scm = case scm of
  890. Error _ -> put'ln'str'ln ";;; Eval error:" *>
  891. put'str'ln (prepend'semicolons (print'scm scm))
  892. _ -> put'ln'str'ln ";;; Eval value:" *>
  893. put'str'ln (print'scm scm)
  894. driver'loop :: SCM -> IO ()
  895. driver'loop env =
  896. put'ln'str'ln ";;; Eval input:" >>
  897. read >>=
  898. \scm ->
  899. case scm of
  900. End'of'File -> put'ln'str'ln ";;; Eval morituri te salutant."
  901. _ -> join (eval <$> pure scm <*> pure env <*> pure env) >>= print >>
  902. driver'loop env
  903. main :: IO ()
  904. main = initial'env >>= driver'loop