fork download
  1. module Main where
  2.  
  3. import qualified Data.Map as M
  4. import Data.Foldable (foldlM)
  5. import System.IO (hFlush, stdout)
  6.  
  7. main = repl newEnvironment
  8.  
  9. flush :: IO ()
  10. flush = hFlush stdout
  11.  
  12. putStrLnF :: String -> IO ()
  13. putStrLnF s = putStrLn s >> flush
  14.  
  15. putStrF :: String -> IO ()
  16. putStrF s = putStr s >> flush
  17.  
  18. printErr :: Env -> String -> IO Env
  19. printErr env err = putStrLnF err >> return env
  20.  
  21. lisp :: Env -> String -> IO Env
  22. lisp env xs = do
  23. case tokenize xs of
  24. Left err -> printErr env err
  25. Right tokens -> lisp' (parse tokens) env
  26.  
  27. lisp' :: ParseResult -> Env -> IO Env
  28. lisp' (Left err) env = putStrLnF err >> return env
  29. lisp' (Right (Just cd, rm)) env = either (printErr env) (lisp'' rm) $ run cd env
  30. lisp' (Right (Nothing, _)) env = putStrLnF "err?" >> return env
  31.  
  32. lisp'' :: [Token] -> (LispData, Env) -> IO Env
  33. lisp'' [] (ld, env) = putStrLnF (toString ld) >> return env
  34. lisp'' rm (ld, env) = putStrLnF (toString ld) >> lisp' (parse rm) env
  35.  
  36.  
  37. repl :: Env -> IO ()
  38. repl env = do
  39. putStrF ">>> "
  40. s <- getLine
  41. case s of
  42. "" -> repl env
  43. ":quit" -> putStrLnF "bye"
  44. ":reset" -> putStrLnF "reset" >> repl newEnvironment
  45. ":{" -> getMultilines "" >>= lisp env >>= repl
  46. (':':_) -> putStrLnF "error (invalid command):" >> repl env
  47. _ -> lisp env s >>= repl
  48.  
  49.  
  50. getMultilines :: String -> IO String
  51. getMultilines s = do
  52. putStrF "==| "
  53. t <- getLine
  54. if t == ":}" then
  55. else
  56. getMultilines $ s ++ " " ++ t
  57.  
  58.  
  59.  
  60. data Token = OpenBracketToken
  61. | CloseBracketToken
  62. | WordToken String
  63. | StringToken String
  64. | BooleanToken Bool
  65. | IntegerToken Integer
  66. | CommaToken
  67. | CommaAtToken
  68. | ConsToken
  69. | QuoteToken
  70. | BackQuoteToken
  71. | CommentToken String
  72. deriving (Show, Eq)
  73.  
  74. trueToken :: Token
  75. trueToken = BooleanToken True
  76.  
  77. falseToken :: Token
  78. falseToken = BooleanToken False
  79.  
  80. untoken :: Token -> String
  81. untoken ConsToken = "."
  82. untoken CommaToken = ","
  83. untoken CommaAtToken = ",@"
  84. untoken QuoteToken = "'"
  85. untoken BackQuoteToken = "`"
  86. untoken OpenBracketToken = "("
  87. untoken CloseBracketToken = ")"
  88. untoken (WordToken s) = s
  89. untoken (StringToken s) = "\"" ++ escape s ++ "\""
  90. untoken (CommentToken s) = ';' : s
  91. untoken (BooleanToken b) = if b then "#t" else "#f"
  92. untoken (IntegerToken i) = show i
  93.  
  94. escape :: String -> String
  95. escape = ($"") . foldl escapeChar id
  96.  
  97. escapeChar :: (String -> String) -> Char -> (String -> String)
  98. escapeChar f '\n' = f.('\\':).('n':)
  99. escapeChar f '\r' = f.('\\':).('r':)
  100. escapeChar f '\t' = f.('\\':).('t':)
  101. escapeChar f '\\' = f.('\\':).('\\':)
  102. escapeChar f '"' = f.('\\':).('"':)
  103. escapeChar f ch = f.(ch:)
  104.  
  105. isDigit :: (Char -> Bool)
  106. isDigit = (`elem` "0123456789")
  107.  
  108. isWordChar :: (Char -> Bool)
  109. isWordChar = not . (`elem` " ;()\"'`,\n\r\t")
  110.  
  111. isWhiteSpace :: (Char -> Bool)
  112. isWhiteSpace = (`elem` " \n\r\t")
  113.  
  114. ltrim :: (String -> String)
  115. ltrim = dropWhile isWhiteSpace
  116.  
  117.  
  118. type TokenizeResult = Either String [Token]
  119.  
  120. tokenize :: String -> TokenizeResult
  121. tokenize [] = Right []
  122. tokenize xs = scan tokenize xs
  123.  
  124. scan :: ((String -> TokenizeResult) -> String -> TokenizeResult)
  125. scan = connect scanOpenBracket
  126. . connect scanCloseBracket
  127. . connect scanString
  128. . connect scanComment
  129. . connect scanQuote
  130. . connect scanWord
  131.  
  132. type ScanResult = Either String (Maybe Token, String)
  133.  
  134. connect :: (String -> ScanResult)
  135. -> (String -> TokenizeResult)
  136. -> (String -> TokenizeResult)
  137. connect f g = next g . f . ltrim
  138.  
  139. next :: (String -> TokenizeResult) -> ScanResult -> TokenizeResult
  140. next _ (Left err) = Left err
  141. next g (Right (tk, zs)) = maybe id (:) tk <$> g zs
  142.  
  143.  
  144. scanOpenBracket :: String -> ScanResult
  145. scanOpenBracket ('(':xs) = Right (Just OpenBracketToken, xs)
  146. scanOpenBracket xs = Right (Nothing, xs)
  147.  
  148.  
  149. scanCloseBracket :: String -> ScanResult
  150. scanCloseBracket (')':xs) = Right (Just CloseBracketToken, xs)
  151. scanCloseBracket xs = Right (Nothing, xs)
  152.  
  153.  
  154. scanString :: String -> ScanResult
  155. scanString ('"':xs) = takeString id xs
  156. scanString xs = Right (Nothing, xs)
  157.  
  158. takeString :: (String -> String) -> String -> ScanResult
  159. takeString f ('\\':ch:xs) = takeString (f.(transSpChar ch:)) xs
  160. takeString f ('\\':_) = Left $ "string token error (illegal escape): " ++ f "\\"
  161. takeString f ('"':xs) = Right (Just $ StringToken $ f "", xs)
  162. takeString f (ch:xs) = takeString (f.(ch:)) xs
  163. takeString f [] = Left $ "string token error (not closed): " ++ f ""
  164.  
  165. transSpChar :: Char -> Char
  166. transSpChar 'n' = '\n'
  167. transSpChar 'r' = '\r'
  168. transSpChar 't' = '\t'
  169. transSpChar ch = ch
  170.  
  171.  
  172. scanComment :: String -> ScanResult
  173. scanComment (';':xs) = takeComment $ break (== '\n') xs
  174. scanComment xs = Right (Nothing, xs)
  175.  
  176. takeComment :: (String, String) -> ScanResult
  177. takeComment (msg, xs) = Right (Just $ CommentToken msg, xs)
  178.  
  179.  
  180. scanQuote :: String -> ScanResult
  181. scanQuote (',':'@':xs) = takeQuote ",@" xs
  182. scanQuote (ch:xs) | elem ch "'`," = takeQuote [ch] xs
  183. scanQuote xs = Right (Nothing, xs)
  184.  
  185. takeQuote :: String -> String -> ScanResult
  186. takeQuote sg xs | validQuoteSyntax xs
  187. = Right (Just $ transQuoteToken sg, xs)
  188. = Left $ "quote token error (no quote target): " ++ sg ++ take 20 xs
  189.  
  190. transQuoteToken :: String -> Token
  191. transQuoteToken "'" = QuoteToken
  192. transQuoteToken "`" = BackQuoteToken
  193. transQuoteToken "," = CommaToken
  194. transQuoteToken ",@" = CommaAtToken
  195. transQuoteToken _ = undefined
  196.  
  197. validQuoteSyntax :: String -> Bool
  198. validQuoteSyntax [] = False
  199. validQuoteSyntax (ch:_) = not $ elem ch "); \n\r\t"
  200.  
  201.  
  202. scanWord :: (String -> ScanResult)
  203. scanWord = takeWord . span isWordChar
  204.  
  205. takeWord :: (String, String) -> ScanResult
  206. takeWord ("", ys) = Right (Nothing, ys)
  207. takeWord (".", ys) = Right (Just ConsToken, ys)
  208. takeWord ("#t", ys) = Right (Just trueToken, ys)
  209. takeWord ("#f", ys) = Right (Just falseToken, ys)
  210. takeWord (w@('-':ch:_), ys) | isDigit ch
  211. = takeNumber w ys
  212. takeWord (w@(ch:_), ys) | isDigit ch
  213. = takeNumber w ys
  214. takeWord (word, ys) = Right (Just $ WordToken word, ys)
  215.  
  216.  
  217. takeNumber :: String -> String -> ScanResult
  218. takeNumber w@('-':ls) ys | all isDigit ls
  219. = Right (Just $ IntegerToken $ read w, ys)
  220. takeNumber w ys | all isDigit w
  221. = Right (Just $ IntegerToken $ read w, ys)
  222. = Left $ "token error (not number): " ++ w
  223.  
  224.  
  225.  
  226. data LispData = List [LispData]
  227. | Cons [LispData] LispData
  228. | Node Token
  229. -- | Quote Token LispData
  230. | Procedure LispData
  231. | Syntax LispData
  232. deriving (Show, Eq)
  233.  
  234. emptyList :: LispData
  235. emptyList = List []
  236.  
  237. trueValue :: LispData
  238. trueValue = Node trueToken
  239.  
  240. falseValue :: LispData
  241. falseValue = Node falseToken
  242.  
  243. intZeroValue :: LispData
  244. intZeroValue = Node $ IntegerToken 0
  245.  
  246. intOneValue :: LispData
  247. intOneValue = Node $ IntegerToken 1
  248.  
  249. toString :: LispData -> String
  250. toString (List xs) = untoken OpenBracketToken
  251. ++ (unwords $ map toString xs)
  252. ++ untoken CloseBracketToken
  253. toString (Cons xs ld) = untoken OpenBracketToken
  254. ++ (unwords $ map toString xs)
  255. ++ " "
  256. ++ untoken ConsToken
  257. ++ " "
  258. ++ toString ld
  259. ++ untoken CloseBracketToken
  260. toString (Node tk) = untoken tk
  261. -- toString (Quote qu ld) = untoken qu ++ toString ld
  262. toString (Procedure (Node (WordToken s))) = "#<procedure " ++ s ++ ">"
  263. toString (Procedure _) = "#<procedure (lambda)>"
  264. toString (Syntax (Node (WordToken s))) = "#<syntax " ++ s ++ ">"
  265.  
  266. type ParseState = (Maybe LispData, [Token])
  267. type ParseResult = Either String ParseState
  268.  
  269. parse :: [Token] -> ParseResult
  270. parse [] = Right (Nothing, [])
  271. parse xs = parseAll xs
  272.  
  273.  
  274. parseAll :: ([Token] -> ParseResult)
  275. parseAll = combine parseList
  276. . combine parseNode
  277. . combine parseQuote
  278. $ const (Left "parse error (parser bug or syntax error):")
  279.  
  280. combine :: ([Token] -> ParseResult)
  281. -> ([Token] -> ParseResult)
  282. -> ([Token] -> ParseResult)
  283. combine f g = nextParse g . f . omit
  284.  
  285. nextParse :: ([Token] -> ParseResult) -> ParseResult -> ParseResult
  286. nextParse p (Right (Nothing, xs)) = p xs
  287. nextParse _ ret = ret
  288.  
  289. isOmitToken :: Token -> Bool
  290. isOmitToken (CommentToken _) = True
  291. isOmitToken _ = False
  292.  
  293. omit :: ([Token] -> [Token])
  294. omit = dropWhile isOmitToken
  295.  
  296.  
  297. parseList :: [Token] -> ParseResult
  298. parseList (OpenBracketToken:xs) = takeList id xs
  299. parseList xs = Right (Nothing, xs)
  300.  
  301. takeList :: ([LispData] -> [LispData]) -> [Token] -> ParseResult
  302. takeList f [] = Left "parse error (not closed):"
  303. takeList f (CloseBracketToken:xs) = Right (Just $ makeListData $ f [], xs)
  304. takeList f (ConsToken:xs) = parse xs >>= takeCons f
  305. takeList f xs = parse xs >>= takeListItem f
  306.  
  307. makeListData :: [LispData] -> LispData
  308. makeListData [] = emptyList
  309. makeListData ls = List ls
  310.  
  311. takeListItem :: ([LispData] -> [LispData]) -> ParseState -> ParseResult
  312. takeListItem f (Just ld, xs) = takeList (f.(ld:)) xs
  313. takeListItem _ (Nothing, _) = Left "parse error (no node):"
  314.  
  315. takeCons :: ([LispData] -> [LispData]) -> ParseState -> ParseResult
  316. takeCons f (Just ld, xs) = makeCons f (f []) ld (omit xs)
  317. takeCons _ (Nothing, _) = Left "parse error (no cons data):"
  318.  
  319.  
  320. makeCons :: ([LispData] -> [LispData]) -> [LispData] -> LispData -> [Token] -> ParseResult
  321. makeCons _ [] _ _ = Left "parse error (invalid cons):"
  322. makeCons f _ (List ls) (CloseBracketToken:xs) = Right (Just $ List (f ls), xs)
  323. makeCons f _ (Cons ls ld) (CloseBracketToken:xs) = Right (Just $ Cons (f ls) ld, xs)
  324. makeCons _ ls ld (CloseBracketToken:xs) = Right (Just $ Cons ls ld, xs)
  325. makeCons _ _ _ _ = Left "parse error (invalid cons):"
  326.  
  327.  
  328. isNodeToken :: Token -> Bool
  329. isNodeToken (WordToken _) = True
  330. isNodeToken (StringToken _) = True
  331. isNodeToken (BooleanToken _) = True
  332. isNodeToken (IntegerToken _) = True
  333. isNodeToken _ = False
  334.  
  335. parseNode :: [Token] -> ParseResult
  336. parseNode (tk:xs) | isNodeToken tk
  337. = Right (Just $ Node tk, xs)
  338. parseNode xs = Right (Nothing, xs)
  339.  
  340.  
  341. isQuoteToken :: Token -> Bool
  342. isQuoteToken QuoteToken = True
  343. isQuoteToken BackQuoteToken = True
  344. isQuoteToken CommaToken = True
  345. isQuoteToken CommaAtToken = True
  346. isQuoteToken _ = False
  347.  
  348. quoteToWord :: Token -> String
  349. quoteToWord QuoteToken = "quote"
  350. quoteToWord BackQuoteToken = "quasiquote"
  351. quoteToWord CommaToken = "unquote"
  352. quoteToWord CommaAtToken = "unquote-splicing"
  353. quoteToWord _ = undefined
  354.  
  355. quoteToNode :: (Token -> LispData)
  356. quoteToNode = Node . WordToken . quoteToWord
  357.  
  358. parseQuote :: [Token] -> ParseResult
  359. parseQuote (tk:xs) | isQuoteToken tk
  360. = parse xs >>= takeQuoteItem tk
  361. parseQuote xs = Right (Nothing, xs)
  362.  
  363. takeQuoteItem :: Token -> ParseState -> ParseResult
  364. -- takeQuoteItem tk (Just ld, xs) = Right (Just $ Quote tk ld, xs)
  365. takeQuoteItem tk (Just ld, xs) = Right (Just $ List [quoteToNode tk, ld], xs)
  366. takeQuoteItem _ (Nothing, _) = Left "parse error (no quote item):"
  367.  
  368.  
  369.  
  370. data Stock = Stock {reserved::Bool, value::LispData} deriving Show
  371. type Env = M.Map String Stock
  372. type RunState = (LispData, Env)
  373. type RunResult = Either String RunState
  374.  
  375. procedures = map (\x -> (x, Stock True $ Procedure $ Node $ WordToken x))
  376. $ ["car", "cdr", "cons" , "pair?", "eq?"
  377. , "+", "*", "-"]
  378.  
  379. syntaxes = map (\x -> (x, Stock True $ Syntax $ Node $ WordToken x))
  380. $ ["quote", "define", "if", "lambda"]
  381.  
  382.  
  383. newEnvironment :: Env
  384. newEnvironment = M.fromList $ procedures ++ syntaxes
  385.  
  386.  
  387. run :: LispData -> Env -> RunResult
  388. run (List ls) env = runList ls env
  389. run ld@(Node _) env = runNode ld env
  390. run ld _ = Left $ "cannot run code: " ++ toString ld
  391.  
  392.  
  393. runNode :: LispData -> Env -> RunResult
  394. runNode (Node (WordToken wd)) env = takeValue env wd $ M.lookup wd env
  395. runNode ld env = Right (ld, env)
  396.  
  397. takeValue :: Env -> String -> Maybe Stock -> RunResult
  398. takeValue _ ky Nothing = Left $ "not found: " ++ ky
  399. takeValue env _ (Just st) = Right (value st, env)
  400.  
  401.  
  402. runList :: [LispData] -> Env -> RunResult
  403. runList [] _ = Left $ "cannot run emptylist:"
  404. runList (ld:xs) env = run ld env >>= takeExecutor xs
  405.  
  406. takeExecutor :: [LispData] -> RunState -> RunResult
  407. takeExecutor ls (Procedure ld, env) = execute ld ls env
  408. takeExecutor ls (Syntax ld, env) = execute ld ls env
  409. takeExecutor _ (ld, _) = Left $ "cannot run code: " ++ toString ld
  410.  
  411.  
  412. execute :: LispData -> [LispData] -> Env -> RunResult
  413. execute (Node (WordToken "quote")) ls env = callQuote ls env
  414. execute (Node (WordToken "car")) ls env = callCar ls env
  415. execute (Node (WordToken "cdr")) ls env = callCdr ls env
  416. execute (Node (WordToken "cons")) ls env = callCons ls env
  417. execute (Node (WordToken "pair?")) ls env = callPairQ ls env
  418. execute (Node (WordToken "eq?")) ls env = callEqQ ls env
  419. execute (Node (WordToken "if")) ls env = callIf ls env
  420. execute (Node (WordToken "define")) ls env = callDefine ls env
  421. execute (Node (WordToken "lambda")) ls env = callLambda ls env
  422. execute (Node (WordToken "+")) ls env = callPlusOp ls env
  423. execute (Node (WordToken "*")) ls env = callAstrOp ls env
  424. execute (Node (WordToken "-")) ls env = callMinusOp ls env
  425. execute (List (la:xs)) ls env = runAll ls env >>= doLambda la xs
  426. execute ld _ _ = Left $ "cannot run code: " ++ toString ld
  427.  
  428. type ArgState = ([LispData], Env)
  429.  
  430. callQuote :: [LispData] -> Env -> RunResult
  431. callQuote (ld:[]) env = Right (ld, env)
  432. callQuote ls _ = Left $ "illigal arguments: " ++ toString (List ls)
  433.  
  434. takeAndRunArgs :: Int -> (ArgState -> RunResult) -> [LispData] -> Env -> RunResult
  435. takeAndRunArgs c f ls env
  436. | length ls == c = takeAndRunAllArgs f ls env
  437. | otherwise = Left $ "illigal arguments: " ++ toString (List ls)
  438.  
  439. takeAndRunArgsWithMin :: Int -> (ArgState -> RunResult) -> [LispData] -> Env -> RunResult
  440. takeAndRunArgsWithMin c f ls env
  441. | length ls >= c = takeAndRunAllArgs f ls env
  442. | otherwise = Left $ "illigal arguments: " ++ toString (List ls)
  443.  
  444. runAll :: [LispData] -> Env -> RunResult
  445. runAll ls env = (\(List xs, e) -> (List $ reverse xs, e)) <$> foldlM runArg (emptyList, env) ls
  446.  
  447. runArg :: RunState -> LispData -> RunResult
  448. runArg (List ls, env) ld = run ld env >>= takeArg ls
  449.  
  450.  
  451. takeArg :: [LispData] -> RunState -> RunResult
  452. takeArg ls (ld, env) = Right (List (ld:ls), env)
  453.  
  454. takeAndRunAllArgs :: (ArgState -> RunResult) -> [LispData] -> Env -> RunResult
  455. takeAndRunAllArgs f ls env = f . unwrapList =<< runAll ls env
  456.  
  457. unwrapList :: RunState -> ArgState
  458. unwrapList (List ls, env) = (ls, env)
  459.  
  460.  
  461. callCar :: ([LispData] -> Env -> RunResult)
  462. callCar = takeAndRunArgs 1 executeCar
  463.  
  464. executeCar :: ArgState -> RunResult
  465. executeCar (List (ld:_) :_, env) = Right (ld, env)
  466. executeCar (Cons (ld:_) _:_, env) = Right (ld, env)
  467. executeCar (ld :_, _ ) = Left $ "illigal argument: " ++ toString ld
  468.  
  469.  
  470. callCdr :: ([LispData] -> Env -> RunResult)
  471. callCdr = takeAndRunArgs 1 executeCdr
  472.  
  473. executeCdr :: ArgState -> RunResult
  474. executeCdr (List (_:[]) :_, env) = Right (emptyList, env)
  475. executeCdr (List (_:ls) :_, env) = Right (List ls, env)
  476. executeCdr (Cons (_:[]) ld:_, env) = Right (ld, env)
  477. executeCdr (Cons (_:ls) ld:_, env) = Right (Cons ls ld, env)
  478. executeCdr (ld :_, _ ) = Left $ "illigal argument: " ++ toString ld
  479.  
  480.  
  481. callCons :: ([LispData] -> Env -> RunResult)
  482. callCons = takeAndRunArgs 2 executeCons
  483.  
  484. executeCons :: ArgState -> RunResult
  485. executeCons (ld:Cons ls la:_, env) = Right (Cons (ld:ls) la, env)
  486. executeCons (ld:List ls :_, env) = Right (List (ld:ls), env)
  487. executeCons (ld:la :_, env) = Right (Cons [ld] la, env)
  488.  
  489.  
  490. callPairQ :: ([LispData] -> Env -> RunResult)
  491. callPairQ = takeAndRunArgs 1 executePairQ
  492.  
  493. executePairQ :: ArgState -> RunResult
  494. executePairQ (Cons _ _ :_, env) = Right (trueValue, env)
  495. executePairQ (List (_:_):_, env) = Right (trueValue, env)
  496. executePairQ (_ , env) = Right (falseValue, env)
  497.  
  498.  
  499. callEqQ :: ([LispData] -> Env -> RunResult)
  500. callEqQ = takeAndRunArgs 2 executeEqQ
  501.  
  502. executeEqQ :: ArgState -> RunResult
  503. executeEqQ (ld1:ld2:_, env) | ld1 == ld2 = Right (trueValue, env)
  504. | otherwise = Right (falseValue, env)
  505.  
  506. callDefine :: [LispData] -> Env -> RunResult
  507. callDefine (Node (WordToken wd):ld:[]) env
  508. | canBind wd env
  509. = bindValue wd <$> run ld env
  510. = Left $ "reserved word: " ++ wd
  511. callDefine ls _ = Left $ "illigal arguments: " ++ toString (List ls)
  512.  
  513. canBind :: (String -> Env -> Bool)
  514. canBind = (maybe True (not.reserved) .) . M.lookup
  515.  
  516. bindValue :: String -> RunState -> RunState
  517. bindValue wd (ld, env) = (ld, M.insert wd (Stock False ld) env)
  518.  
  519.  
  520. callIf :: [LispData] -> Env -> RunResult
  521. callIf (test:tv:fv:[]) env = run test env >>= executeIf tv fv
  522. callIf (test:tv:[]) env = run test env >>= executeIf tv falseValue
  523. callIf ls _ = Left $ "invalid arguments: " ++ toString (List ls)
  524.  
  525. executeIf :: LispData -> LispData -> RunState -> RunResult
  526. executeIf _ fv (Node (BooleanToken False), env) = run fv env
  527. executeIf tv _ (_, env) = run tv env
  528.  
  529.  
  530. callLambda :: [LispData] -> Env -> RunResult
  531. callLambda ls@(Node (WordToken _):_:_) env = Right (Procedure (List ls), env)
  532. callLambda ls@(List xs :_:_) env | checkArgsAllWord xs
  533. = Right (Procedure (List ls), env)
  534. callLambda ls@(Cons xs ld :_:_) env | checkArgsAllWord (ld:xs)
  535. = Right (Procedure (List ls), env)
  536. callLambda ls _ = Left $ "invalid arguments: " ++ toString (List ls)
  537.  
  538. checkArgsAllWord :: ([LispData] -> Bool)
  539. checkArgsAllWord = all isWordTokenNode
  540.  
  541. isWordTokenNode (Node (WordToken _)) = True
  542. isWordTokenNode _ = False
  543.  
  544. doLambda :: LispData -> [LispData] -> RunState -> RunResult
  545. doLambda (Node (WordToken wd)) xs st@(_, env) = bindOneLArg wd st >>= evalLambda env xs
  546. doLambda (List ls) xs st@(_, env) = bindListLArg ls st >>= evalLambda env xs
  547. doLambda (Cons ls ed) xs st@(_, env) = bindConsLArg ls ed st >>= evalLambda env xs
  548.  
  549. bindOneLArg :: String -> RunState -> RunResult
  550. bindOneLArg wd st@(_, env) | canBind wd env
  551. = Right $ bindValue wd st
  552. = Left $ "reserved word: " ++ wd
  553.  
  554. bindListLArg :: [LispData] -> RunState -> RunResult
  555. bindListLArg ls st@(List args, env) | length args == length ls
  556. = foldlM fListBind st $ zip ls args
  557. = Left $ "invalid number of arguments: "
  558.  
  559. fListBind :: RunState -> (LispData, LispData) -> RunResult
  560. fListBind (_, env) (Node (WordToken wd), ld) | canBind wd env
  561. = Right $ bindValue wd (ld, env)
  562. = Left $ "reserved word: " ++ wd
  563.  
  564. bindConsLArg :: [LispData] -> LispData -> RunState -> RunResult
  565. bindConsLArg ls (Node (WordToken wd)) (List args, env)
  566. | length args >= length ls
  567. = fConsBind ls wd env $ splitAt (length ls) args
  568. = Left $ "invalid number of arguments: "
  569.  
  570. fConsBind :: [LispData] -> String -> Env -> ([LispData], [LispData]) -> RunResult
  571. fConsBind ls wd env (args, []) = bindListLArg ls (List args, env) >>= \(_, e) -> bindOneLArg wd (emptyList, e)
  572. fConsBind ls wd env (args, rm) = bindListLArg ls (List args, env) >>= \(_, e) -> bindOneLArg wd (List rm, e)
  573.  
  574. evalLambda :: Env -> [LispData] -> RunState -> RunResult
  575. evalLambda env xs st = (\(res,_) -> (res, env)) <$> foldlM fLambda st xs
  576.  
  577. fLambda :: RunState -> LispData -> RunResult
  578. fLambda (_, env) code = run code env
  579.  
  580.  
  581. callPlusOp :: ([LispData] -> Env -> RunResult)
  582. callPlusOp = takeAndRunAllArgs executePlusOp
  583.  
  584. executePlusOp :: ArgState -> RunResult
  585. executePlusOp (ls, env) = foldlM sumNums (intZeroValue, env) ls
  586.  
  587. sumNums :: RunState -> LispData -> RunResult
  588. sumNums (Node (IntegerToken v1), env) (Node (IntegerToken v2)) = Right (Node $ IntegerToken $ v1 + v2, env)
  589. sumNums _ ld = Left $ "invalid arguments: " ++ toString ld
  590.  
  591.  
  592. callAstrOp :: ([LispData] -> Env -> RunResult)
  593. callAstrOp = takeAndRunAllArgs executeAstrOp
  594.  
  595. executeAstrOp :: ArgState -> RunResult
  596. executeAstrOp (ls, env) = foldlM mulNums (intOneValue, env) ls
  597.  
  598. mulNums :: RunState -> LispData -> RunResult
  599. mulNums (Node (IntegerToken v1), env) (Node (IntegerToken v2)) = Right (Node $ IntegerToken $ v1 * v2, env)
  600. mulNums _ ld = Left $ "invalid arguments: " ++ toString ld
  601.  
  602.  
  603.  
  604. callMinusOp :: ([LispData] -> Env -> RunResult)
  605. callMinusOp = takeAndRunArgsWithMin 1 executeMinusOp
  606.  
  607. executeMinusOp :: ArgState -> RunResult
  608. executeMinusOp (Node (IntegerToken v):[], env) = Right (Node $ IntegerToken (0 - v), env)
  609. executeMinusOp (fv:ls, env) = foldlM subNums (fv, env) ls
  610.  
  611. subNums :: RunState -> LispData -> RunResult
  612. subNums (Node (IntegerToken v1), env) (Node (IntegerToken v2)) = Right (Node $ IntegerToken $ v1 - v2, env)
  613. subNums _ ld = Left $ "invalid arguments: " ++ toString ld
  614.  
  615.  
  616.  
  617.  
  618.  
  619.  
Success #stdin #stdout 0s 8388607KB
stdin
(define f (lambda (x y) (cons (car x) (cdr y))))
(f '(a b c) '(x y z))
(if (pair? '(a b c)) 'hoge 'piyo)
(if (eq? 'a 'a) 'foo 'bar)
(* (+ 1 2 3) (- 10 4 3) 10)
:quit
stdout
>>> #<procedure (lambda)>
>>> (a y z)
>>> hoge
>>> foo
>>> 180
>>> bye