Now works as a simple calculator of s-exprs, with + - * / and numbers

This commit is contained in:
Suguivy 2021-02-24 18:50:53 +01:00
parent a00e5b46be
commit a02176ee18
6 changed files with 108 additions and 3 deletions

View File

@ -17,4 +17,10 @@ executable picolisp
hs-source-dirs: src hs-source-dirs: src
main-is: Main.hs main-is: Main.hs
default-language: Haskell2010 default-language: Haskell2010
build-depends: base >= 4.7 && < 5 other-modules: Parser,
Evaluator,
Expression,
Primitives
build-depends: base >= 4.7 && < 5,
parsec,
haskeline

19
src/Evaluator.hs Normal file
View File

@ -0,0 +1,19 @@
module Evaluator where
import Expression
import Primitives
eval :: Expression -> Expression
eval n@(Number _) = n
eval s@(Symbol _) = s
eval (SExpr es) = apply $ SExpr (map eval es)
apply :: Expression -> Expression
apply (SExpr (f:args)) = case f of
Symbol "+" -> pAdd args
Symbol "-" -> pSub args
Symbol "*" -> pMul args
Symbol "/" -> pDiv args
Symbol _ -> error "no primitive functions not supported"
_ -> error "object not applicable"
apply _ = error "Object not applicable"

14
src/Expression.hs Normal file
View File

@ -0,0 +1,14 @@
module Expression where
-- The basic unit of the language are Expressions, so we make an
-- Expression data with the primitive expression values as the
-- constructors
data Expression = Number Double
| Symbol String
| SExpr [Expression]
deriving (Eq)
instance Show Expression where
show (Number n) = show n
show (Symbol s) = show s
show (SExpr es) = "(" ++ unwords (map show es) ++ ")"

View File

@ -1,5 +1,21 @@
module Main where module Main where
import Parser
import Evaluator
import System.Console.Haskeline
main :: IO () main :: IO ()
main = do main = runInputT defaultSettings repl
putStrLn "hello world" where repl = do
line <- getInputLine "picolisp> "
case line of
Nothing -> return ()
Just s -> do
let eithE = parseExpression s
case eithE of
Left err -> do
outputStrLn $ show err
repl
Right e -> do
outputStrLn . show $ eval e
repl

40
src/Parser.hs Normal file
View File

@ -0,0 +1,40 @@
module Parser where
import Text.ParserCombinators.Parsec
import Data.Char
import Expression
-- Receives a string, parses it, and returns an expression
parseExpression :: String -> Either ParseError Expression
parseExpression s = do
e <- parse anyExpressionParser "" s
return e
-- Parser any possible expression
anyExpressionParser :: GenParser Char st Expression
anyExpressionParser = numberParser <|> symbolParser <|> sexprParser
-- Parses an S-expr
sexprParser :: GenParser Char st Expression
sexprParser = do
_ <- spaces
_ <- char '('
es <- many1 (try $ spaces >> anyExpressionParser)
_ <- spaces
_ <- char ')' >> spaces
return $ SExpr es
-- Parses a symbol
symbolParser :: GenParser Char st Expression
symbolParser = Symbol <$> do
c <- symbolChar
cs <- many (symbolChar <|> digit)
return $ c:cs
where symbolChar = letter <|> oneOf "!#$%&*+-/:<=>?@\\^_`~"
-- Parses a number
numberParser :: GenParser Char st Expression
numberParser = do
num <- read <$> many1 digit
return $ Number num

10
src/Primitives.hs Normal file
View File

@ -0,0 +1,10 @@
module Primitives where
import Expression
pAdd, pSub, pMul, pDiv :: [Expression] -> Expression
pAdd = foldr1 $ \(Number a) (Number b) -> Number (a+b)
pSub = foldr1 $ \(Number a) (Number b) -> Number (a-b)
pMul = foldr1 $ \(Number a) (Number b) -> Number (a*b)
pDiv = foldr1 $ \(Number a) (Number b) -> Number (a/b)