···3434metaFuncs =
3535 M.fromList
3636 [ ("define", defineFunc),
3737- ("undefine", unDefineFunc),
3737+ ("undef", unDefFunc),
3838 ("lambda", lambdaFunc),
3939 ("if", ifFunc),
4040 ("eval", evalFunc),
4141- ("quote", quoteFunc)
4141+ ("quote", quoteFunc),
4242+ ("get", getFunc),
4343+ ("import", importFunc)
4244 ]
43454446defineFunc :: Env -> [NlsAstValue] -> Eval (NlsRunValue, Env)
···5557 Right env'' -> pureWithEnv (RUnit) env''
5658defineFunc _ _ = Left "define expects a symbol and expression"
57595858-unDefineFunc :: Env -> [NlsAstValue] -> Eval (NlsRunValue, Env)
5959-unDefineFunc env [ASymbol name] =
6060+unDefFunc :: Env -> [NlsAstValue] -> Eval (NlsRunValue, Env)
6161+unDefFunc env [ASymbol name] =
6062 case undefine name env of
6163 Left err -> Left err
6264 Right env' -> pureWithEnv (RUnit) env'
6363-unDefineFunc _ _ = Left "undefine expects a symbol"
6565+unDefFunc _ _ = Left "undef expects a symbol"
64666567lambdaFunc :: Env -> [NlsAstValue] -> Eval (NlsRunValue, Env)
6668lambdaFunc env [AList params, body] = do
6769 paramNames <- mapM extractParam params
6868- let fn = createLambda paramNames [body]
7070+ let fn = createLambda env paramNames [body]
6971 pureWithEnv fn env
7072lambdaFunc _ _ = Left "lambda expects a parameter and body list"
7173···9294quoteFunc env [expr] = pureWithEnv (aToRValue expr) env
9395quoteFunc _ _ = Left "quote expects one argument"
94969797+getFunc :: Env -> [NlsAstValue] -> Eval (NlsRunValue, Env)
9898+getFunc env [mExpr, AString key] = do
9999+ (mVal, _) <- eval env mExpr
100100+ case mVal of
101101+ RModule env' ->
102102+ case lookupEnv key env' of
103103+ Just val -> pureWithEnv val env
104104+ Nothing -> Left $ "module does not contain: " <> T.pack key
105105+ _ -> Left "expected module"
106106+getFunc _ _ = Left "get expects a module and a string"
107107+108108+importFunc :: Env -> [NlsAstValue] -> Eval (NlsRunValue, Env)
109109+importFunc _ _ = Left "cannot call raw import"
110110+95111-- EVALUATION --
9611297113apply :: NlsRunValue -> [NlsRunValue] -> Env -> Eval (NlsRunValue, Env)
···104120apply _ _ _ = Left "attempted to call non-function"
105121106122-- create a RFunction from a lambda expression
107107-createLambda :: [String] -> [NlsAstValue] -> NlsRunValue
108108-createLambda params body = RFunction wrapper
123123+createLambda :: Env -> [String] -> [NlsAstValue] -> NlsRunValue
124124+createLambda env params body = RFunction wrapper
109125 where
110126 wrapper :: [NlsRunValue] -> Env -> Eval (NlsRunValue, Env)
111111- wrapper args env = do
127127+ wrapper args _ = do
112128 if length args /= length params
113129 then
114130 Left
···122138 let env' = mergeFrame newFrame env
123139 evalProgram env' body
124140141141+-- evaluate a single ast value
125142eval :: Env -> NlsAstValue -> Eval (NlsRunValue, Env)
126143eval env (ANumber n) = pureWithEnv (RNumber n) env
127144eval env (AString s) = pureWithEnv (RString s) env
···146163 (vals, env'') <- mapAccumM eval env' args
147164 apply func vals env''
148165166166+-- evaluate a sequence of ast values
149167evalProgram :: Env -> [NlsAstValue] -> Eval (NlsRunValue, Env)
150168evalProgram env [] = pure (RUnit, env)
151169evalProgram env [x] = eval env x
+71-9
src/Main.hs
···11+{-# LANGUAGE OverloadedStrings #-}
22+13module Main (main) where
2435import qualified Data.Text as T
46import qualified Data.Text.IO as TIO
57import qualified Eval as E
68import qualified Parser as P
77-import qualified Repl as R
89import System.Environment (getArgs)
1010+import System.IO
1111+ ( hFlush,
1212+ isEOF,
1313+ stdout,
1414+ )
915import Types
1616+import qualified Util as U
1717+1818+-- load, parse, and evaluate a file
1919+evalFile :: Env -> FilePath -> IO (Eval (NlsRunValue, Env))
2020+evalFile env path = do
2121+ t <- TIO.readFile path
2222+ case P.parse t of
2323+ Left err -> pure $ Left err
2424+ Right ast -> evalTop env ast
2525+2626+-- evaluate top level ast, handling import
2727+evalTop :: Env -> [NlsAstValue] -> IO (Eval (NlsRunValue, Env))
2828+evalTop env [] = pure $ Right (RUnit, env)
2929+evalTop env [x] =
3030+ case x of
3131+ AList (ASymbol "import" : [AString path, ASymbol name]) -> do
3232+ res <- evalFile E.baseEnv path
3333+ case res of
3434+ Left err -> pure $ Left err
3535+ Right (_, env') ->
3636+ case U.define name (RModule env') env of
3737+ Left err -> pure $ Left err
3838+ Right env'' -> pure $ Right (RUnit, env'')
3939+ AList (ASymbol "import" : _) -> pure $ Left "import expects a file path and symbol"
4040+ _ -> pure $ E.eval env x
4141+evalTop env (x : xs) = do
4242+ res <- evalTop env [x]
4343+ case res of
4444+ Left err -> pure $ Left err
4545+ Right (_, env') -> evalTop env' xs
4646+4747+-- evaluate and print AST
4848+evalMany :: Env -> [NlsAstValue] -> IO Env
4949+evalMany env [] = pure env
5050+evalMany env (x : xs) = do
5151+ res <- evalTop env [x]
5252+ case res of
5353+ Left err -> do
5454+ putStrLn $ "Error: " ++ T.unpack err
5555+ pure env
5656+ Right (val, env') -> do
5757+ print val
5858+ evalMany env' xs
5959+6060+repl :: Env -> IO ()
6161+repl env = do
6262+ putStr "nls> "
6363+ hFlush stdout
6464+ eof <- isEOF
6565+ if eof
6666+ then putStrLn "\nGoodbye!"
6767+ else do
6868+ line <- getLine
6969+ let input = T.pack line
7070+ newEnv <- case P.parse input of
7171+ Left err -> do
7272+ putStrLn $ "Parse error: " ++ (T.unpack err)
7373+ pure env
7474+ Right ast -> evalMany env ast
7575+ repl newEnv
10761177main :: IO ()
1278main = do
1379 args <- getArgs
1480 case args of
1581 [filePath] -> do
1616- t <- TIO.readFile filePath
1717- case P.parse t of
8282+ res <- evalFile E.baseEnv filePath
8383+ case res of
1884 Left err -> putStrLn (T.unpack err)
1919- Right ast -> do
2020- case E.evalProgram E.baseEnv ast of
2121- Left err -> putStrLn (T.unpack err)
2222- Right (RUnit, env) -> R.repl env
2323- Right (result, _) -> print result
2424- _ -> R.repl E.baseEnv
8585+ Right (val, _) -> print val
8686+ _ -> repl E.baseEnv
+23-2
src/Parser.hs
···3232symbol :: T.Text -> NlsParser T.Text
3333symbol = L.symbol sc
34343535+-- split a string with character delimiter
3636+splitOn :: Char -> String -> [String]
3737+splitOn c str =
3838+ case break (== c) str of
3939+ (before, []) -> [before]
4040+ (before, _ : after) -> before : splitOn c after
4141+4242+-- make `get` ast
4343+makeGet :: NlsAstValue -> String -> NlsAstValue
4444+makeGet acc key = AList [ASymbol "get", acc, AString key]
4545+4646+-- desugar dotted expr into `get` sequence
4747+desugarDotted :: String -> NlsAstValue
4848+desugarDotted s =
4949+ case splitOn '.' s of
5050+ [] -> AString s -- this should never happen
5151+ (x : xs) -> foldl makeGet (ASymbol x) xs
5252+3553parseSymbol :: NlsParser NlsAstValue
3654parseSymbol = do
3755 first <- letterChar <|> oneOf ("!$%&|*+-/:<=>?@^_~" :: String)
3838- rest <- many (alphaNumChar <|> oneOf ("!$%&|*+-/:<=>?@^_~" :: String))
3939- pure $ ASymbol (first : rest)
5656+ rest <- many (alphaNumChar <|> oneOf ("!$%&|*+-/:<=>?@^_~." :: String))
5757+ let sym = first : rest
5858+ if elem '.' sym
5959+ then pure $ desugarDotted sym
6060+ else pure $ ASymbol sym
40614162parseQuote :: NlsParser NlsAstValue
4263parseQuote = do
-40
src/Repl.hs
···11-module Repl (repl) where
22-33-import qualified Data.Text as T
44-import qualified Eval as E
55-import qualified Parser as P
66-import System.IO
77- ( hFlush,
88- isEOF,
99- stdout,
1010- )
1111-import Types
1212-1313--- evaluate and print AST
1414-evalMany :: Env -> [NlsAstValue] -> IO Env
1515-evalMany env [] = pure env
1616-evalMany env (x : xs) =
1717- case E.eval env x of
1818- Left err -> do
1919- putStrLn $ "Error: " ++ T.unpack err
2020- pure env
2121- Right (val, env') -> do
2222- print val
2323- evalMany env' xs
2424-2525-repl :: Env -> IO ()
2626-repl env = do
2727- putStr "nls> "
2828- hFlush stdout
2929- eof <- isEOF
3030- if eof
3131- then putStrLn "\nGoodbye!"
3232- else do
3333- line <- getLine
3434- let input = T.pack line
3535- newEnv <- case P.parse input of
3636- Left err -> do
3737- putStrLn $ "Parse error: " ++ (T.unpack err)
3838- pure env
3939- Right ast -> evalMany env ast
4040- repl newEnv
+2
src/Types.hs
···3333 | RSymbol String
3434 | RList [NlsRunValue]
3535 | RFunction ([NlsRunValue] -> Env -> Eval (NlsRunValue, Env))
3636+ | RModule Env
3637 | RUnit -- an empty value
37383839instance Show NlsRunValue where
···4243 show (RSymbol s) = s
4344 show (RList xs) = "(" ++ unwords (map show xs) ++ ")"
4445 show (RFunction _) = "<function>"
4646+ show (RModule _) = "<module>"
4547 show RUnit = "<empty>"
+6
src/Util.hs
···55 rToAValue,
66 lookupEnv,
77 define,
88+ defineUnchecked,
89 undefine,
910 mergeFrame,
1011 mapAccumM,
···3536rToAValue (RBool False) = pure $ ASymbol "false"
3637rToAValue (RList xs) = AList <$> mapM rToAValue xs
3738rToAValue (RFunction _) = Left "cannot eval a function"
3939+rToAValue (RModule _) = Left "cannot eval a module"
3840rToAValue RUnit = Left "cannot eval an empty unit"
39414042-- lookup a key recursively in an environment
···5052 case M.lookup key (frame env) of
5153 Just _ -> Left ("cannot shadow binding " <> T.pack key <> " in current frame")
5254 Nothing -> Right env {frame = M.insert key val (frame env)}
5555+5656+-- define without checking shadow rules
5757+defineUnchecked :: String -> NlsRunValue -> Env -> Env
5858+defineUnchecked key val env = env {frame = M.insert key val (frame env)}
53595460-- remove a binding from the current frame
5561undefine :: String -> Env -> Either T.Text Env
+1
std/std.nls
···55(define fold (lambda (f b x) (if x (f (car x) (fold f b (cdr x))) b)))
66(define inc (lambda (x) (+ x 1)))
77(define dec (lambda (x) (- x 1)))
88+