aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md3
-rw-r--r--Setup.hs2
-rw-r--r--bin/Main.hs26
-rw-r--r--cabal.project.local1
-rw-r--r--default.nix10
-rw-r--r--lisk.cabal55
-rw-r--r--release.nix4
-rw-r--r--shell.nix40
-rw-r--r--src/Evaluator.hs22
-rw-r--r--src/Operators.hs22
-rw-r--r--src/Parser.hs101
-rw-r--r--tests/Main.hs8
-rw-r--r--tests/Properties.hs15
13 files changed, 309 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..0b5e45b
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,3 @@
1# Revision history for lisk
2
3<!-- populate this as we progress -->
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
index 0000000..9a994af
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
1import Distribution.Simple
2main = defaultMain
diff --git a/bin/Main.hs b/bin/Main.hs
new file mode 100644
index 0000000..2942566
--- /dev/null
+++ b/bin/Main.hs
@@ -0,0 +1,26 @@
1module Main where
2
3import Evaluator (eval)
4import Parser (Expr (..), parseLispValue)
5import System.Console.Readline
6import Text.ParserCombinators.Parsec
7
8readExpr :: String -> Expr
9readExpr inp =
10 case parse parseLispValue "(unknown)" inp of
11 Left err -> StringLiteral $ show err
12 Right val -> val
13
14repl :: IO ()
15repl = do
16 inp <- readline "(lisk)> "
17 case inp of
18 Nothing -> return ()
19 Just ",q" -> return ()
20 Just line -> do
21 addHistory line
22 print . eval . readExpr $ line
23 repl
24
25main :: IO ()
26main = repl
diff --git a/cabal.project.local b/cabal.project.local
new file mode 100644
index 0000000..8909668
--- /dev/null
+++ b/cabal.project.local
@@ -0,0 +1 @@
tests: True
diff --git a/default.nix b/default.nix
new file mode 100644
index 0000000..1890d35
--- /dev/null
+++ b/default.nix
@@ -0,0 +1,10 @@
1{ mkDerivation, base, parsec, readline, stdenv }:
2mkDerivation {
3 pname = "lisk";
4 version = "0.1.0.0";
5 src = ./.;
6 isLibrary = false;
7 isExecutable = true;
8 executableHaskellDepends = [ base parsec readline ];
9 license = stdenv.lib.licenses.gpl3;
10}
diff --git a/lisk.cabal b/lisk.cabal
new file mode 100644
index 0000000..627815d
--- /dev/null
+++ b/lisk.cabal
@@ -0,0 +1,55 @@
1cabal-version: >=1.10
2-- Initial package description 'lisk.cabal' generated by 'cabal init'. For
3-- further documentation, see http://haskell.org/cabal/users-guide/
4
5name: lisk
6version: 0.1.0.0
7synopsis: a lisp interpreter
8description: an educational lisp interpreter written in haskell by DSCRV
9-- bug-reports:
10license: GPL-3
11-- license-file: LICENSE
12author: Akshay
13maintainer: [email protected]
14-- copyright:
15-- category:
16build-type: Simple
17extra-source-files: CHANGELOG.md
18
19library
20 default-language: Haskell2010
21 build-depends:
22 base >=4.12 && <4.13,
23 parsec == 3.*,
24 mtl >= 2.1
25 hs-source-dirs: src
26 exposed-modules:
27 Parser,
28 Evaluator,
29 Operators
30
31executable lisk
32 default-language: Haskell2010
33 main-is: Main.hs
34 build-depends:
35 base >=4.12 && <4.13,
36 parsec == 3.*,
37 readline >= 1.0,
38 lisk
39 hs-source-dirs: bin
40
41test-suite properties
42 default-language: Haskell2010
43 type: exitcode-stdio-1.0
44 main-is: Main.hs
45 hs-source-dirs: tests
46 build-depends:
47 base >=4.12 && <4.13,
48 parsec == 3.*,
49 QuickCheck >= 2.4 && < 3,
50 test-framework >= 0.6 && < 0.9,
51 test-framework-hunit >= 0.3 && < 0.5,
52 test-framework-quickcheck2 >= 0.2 && < 0.4,
53 test-framework-th >= 0.2 && < 0.4,
54 lisk
55 other-modules: Properties
diff --git a/release.nix b/release.nix
new file mode 100644
index 0000000..8180314
--- /dev/null
+++ b/release.nix
@@ -0,0 +1,4 @@
1let
2 pkgs = import <nixpkgs> {};
3in
4 pkgs.haskellPackages.callPackage ./default.nix {}
diff --git a/shell.nix b/shell.nix
new file mode 100644
index 0000000..b1a597f
--- /dev/null
+++ b/shell.nix
@@ -0,0 +1,40 @@
1let
2
3 all-hies = fetchTarball {
4 url = "https://github.com/infinisil/all-hies/tarball/534ac517b386821b787d1edbd855b9966d0c0775";
5 sha256 = "0bw1llpwxbh1dnrnbxkj2l0j58s523hjivszf827c3az5i4py1i2";
6 };
7
8 pkgs = import <nixpkgs> {
9 # Pass no config for purity
10 config = {};
11 overlays = [
12 (import all-hies {}).overlay
13 ];
14 };
15
16 inherit (pkgs) haskellPackages;
17
18 haskellDeps = ps: with ps; [
19 base
20 lens
21 parsec
22 mtl
23 readline
24 ];
25
26 ghc = haskellPackages.ghcWithPackages haskellDeps;
27
28 externalPackages = [
29 ghc
30 pkgs.gdb
31 pkgs.cabal2nix
32 haskellPackages.cabal-install
33 haskellPackages.hie
34 haskellPackages.hoogle
35 ];
36in
37pkgs.stdenv.mkDerivation {
38 name = "env";
39 buildInputs = externalPackages;
40}
diff --git a/src/Evaluator.hs b/src/Evaluator.hs
new file mode 100644
index 0000000..f264ee0
--- /dev/null
+++ b/src/Evaluator.hs
@@ -0,0 +1,22 @@
1module Evaluator (
2 eval
3 ) where
4
5import Operators
6import Parser
7import Text.ParserCombinators.Parsec
8
9apply :: String -> [Expr] -> Expr
10apply fn args =
11 case lookup fn primitives of
12 Just f -> f args
13 _ -> BoolLiteral False -- TODO: error out instead
14
15eval :: Expr -> Expr
16eval v@(StringLiteral s) = v
17eval v@(IntLiteral i) = v
18eval v@(BoolLiteral b) = v
19-- handle quotes as literals
20eval (List[Id "quote", val]) = val
21eval (List (Id fn : args)) = apply fn $ map eval args
22
diff --git a/src/Operators.hs b/src/Operators.hs
new file mode 100644
index 0000000..e57f885
--- /dev/null
+++ b/src/Operators.hs
@@ -0,0 +1,22 @@
1module Operators (
2 primitives
3 ) where
4
5import Parser
6
7primitives :: [(String, [Expr] -> Expr)]
8primitives =
9 [
10 ("+", arithmetic (+))
11 , ("-", arithmetic (-))
12 , ("*", arithmetic (*))
13 , ("/", arithmetic div)
14 ]
15
16arithmetic :: (Integer -> Integer -> Integer) -> [Expr] -> Expr
17arithmetic op args = IntLiteral $ foldl1 op $ map unwrapNum args
18
19unwrapNum :: Expr -> Integer
20unwrapNum (IntLiteral n) = n
21unwrapNum _ = undefined
22
diff --git a/src/Parser.hs b/src/Parser.hs
new file mode 100644
index 0000000..dcbfdb1
--- /dev/null
+++ b/src/Parser.hs
@@ -0,0 +1,101 @@
1module Parser ( parseLispValue
2 , Expr(..)
3 , parseString
4 , parseInt
5 , parseFloat
6 , parseAtom
7 , parseList
8 , parseQuote
9 , parseDottedList
10 ) where
11
12import Control.Applicative ((<$>))
13import Control.Monad (liftM)
14import Text.ParserCombinators.Parsec
15
16
17type Ident = String
18
19data Expr = List [Expr]
20 | DottedList [Expr] Expr
21 | StringLiteral String
22 | IntLiteral Integer
23 | FloatLiteral Double
24 | BoolLiteral Bool
25 | Id Ident
26 deriving (Eq)
27
28parseString :: Parser Expr
29parseString = do
30 char '"'
31 innards <- many (noneOf "\"")
32 char '"'
33 return (StringLiteral innards)
34
35parseInt :: Parser Expr
36parseInt = IntLiteral . read <$> many1 digit
37
38parseFloat :: Parser Expr
39parseFloat = do
40 characteristic <- many digit
41 char '.'
42 mantissa <- many1 digit
43 return $ (FloatLiteral . read) $ characteristic ++ "." ++ mantissa
44
45symbol :: Parser Char
46symbol = oneOf "!#$%&|*+:/-=<?>@^_~"
47
48parseAtom :: Parser Expr
49parseAtom = do
50 first <- letter <|> symbol
51 rest <- many (letter <|> symbol <|> digit)
52 let atom = first:rest
53 return $ case atom of
54 "#t" -> BoolLiteral True
55 "#f" -> BoolLiteral False
56 _ -> Id atom
57
58whiteSpace :: Parser ()
59whiteSpace = skipMany1 space
60
61parseList :: Parser Expr
62parseList = List <$> sepBy parseLispValue whiteSpace
63
64parseDottedList :: Parser Expr
65parseDottedList = do
66 head <- endBy parseLispValue whiteSpace
67 char '.'
68 whiteSpace
69 DottedList head <$> parseLispValue
70
71parseQuote :: Parser Expr
72parseQuote = do
73 char '\''
74 x <- parseLispValue
75 return $ List [Id "quote", x]
76
77
78parseLispValue :: Parser Expr
79parseLispValue =
80 try parseAtom
81 <|> parseString
82 <|> parseInt
83 <|> parseQuote
84 -- TODO: figure out a way to have floats and dotted lists
85 -- <|> parseFloat
86 <|> do
87 char '('
88 x <- try parseList <|> parseDottedList
89 char ')'
90 return x
91 <?> "expected lisp value!"
92
93instance Show Expr where
94 show (DottedList xs x) = "(" ++ unwords (map show xs) ++ " . " ++ show x ++ ")"
95 show (List xs) = "(" ++ unwords (map show xs) ++ ")"
96 show (StringLiteral s) = "\"" ++ s ++ "\""
97 show (IntLiteral n) = show n
98 show (FloatLiteral n) = show n
99 show (BoolLiteral True) = "#t"
100 show (BoolLiteral False) = "#f"
101 show (Id i) = i
diff --git a/tests/Main.hs b/tests/Main.hs
new file mode 100644
index 0000000..a05ddb1
--- /dev/null
+++ b/tests/Main.hs
@@ -0,0 +1,8 @@
1module Main where
2
3import Properties
4import Test.Framework.Providers.QuickCheck2
5import Test.Framework.Runners.Console (defaultMain)
6import Test.QuickCheck
7
8main = defaultMain [ Properties.tests ]
diff --git a/tests/Properties.hs b/tests/Properties.hs
new file mode 100644
index 0000000..b42cd4b
--- /dev/null
+++ b/tests/Properties.hs
@@ -0,0 +1,15 @@
1{-# LANGUAGE TemplateHaskell #-}
2module Properties where
3
4import Parser (Expr (..),
5 parseLispValue,
6 parseQuote)
7
8import Test.Framework.Providers.QuickCheck2
9import Test.Framework.TH
10import Test.QuickCheck
11import Text.ParserCombinators.Parsec
12
13-- some tests would go here hopefully
14
15tests = $testGroupGenerator