added more experiments, mostly failed, see readme.

This commit is contained in:
Raphael Jacobs 2022-04-21 22:25:46 +02:00
parent 4997f9fb3d
commit 66022a0438
10 changed files with 444 additions and 26 deletions

View File

@ -3,3 +3,7 @@
This repository contains (will contain) a bunch of unsorted interpreters for various "flavors" of lambda calculus, lisp languages and alike. It's a collection of experiments in implementing interpreters and compilers.
Currently, only a simple untyped lambda calculus is implemented, together with a beta reduction thing. More will be surely added in the future.
Typed-1 is an experiment at writing a lisp evaluator with as many type guarantees as possible, too many. You can only build the AST if everything has the correct type, which is a terrible idea and makes parsing very tricky (could not write a parser, probably just need to throw some 20 more ghc extensions at it).
Typed-0 is a work-in-progress regular lisp interpreter without the fancy type stuff.

View File

@ -22,6 +22,7 @@ description: Please see the README on GitHub at <https://github.com/gith
dependencies:
- base >= 4.7 && < 5
- attoparsec
- megaparsec
- containers
- text
- repline
@ -33,6 +34,7 @@ library:
source-dirs:
- src
- src/Untyped-0
- src/Typed-1
executables:
lambdatest-exe:

View File

@ -3,18 +3,18 @@ module GenericLambda where
-- dumb attempt to generalize my lambda calculus functions
-- data instance Lambda UntypedLambda = UL UL.Environment UL.Expression UL.
class GenericLambda l where
data Env l
type Iden l
type Data l
data PRes l
emptyEnv :: Env l
updateEnv :: Env l -> Iden l -> Data l -> Env l
envStrings :: Env l -> [(String, String)]
normalform :: Env l -> Data l -> Either String String
execLine :: Env l -> String -> IO (Maybe String, Env l)
-- This typeclass encompasses a generic "lambda calculus" language, but it can be
-- more flexible than that.
class GenericLambda l where
data Env l -- The type encompassing the environment, and actually the entire state of the language.
type Iden l -- The type of identifiers.
type Data l -- The type representing usually an expression + type information if necessary.
data PRes l -- the type representing a parsing result.
emptyEnv :: Env l -- the empty environment.
updateEnv :: Env l -> Iden l -> Data l -> Env l -- function to update the environment.
envStrings :: Env l -> [(String, String)] -- function to show the environment for tab completions.
normalform :: Env l -> Data l -> Either String String -- function to reduce some expression.
execLine :: Env l -> String -> IO (Maybe String, Env l) -- function to execute 1 line of "code".
evalPrint' :: (GenericLambda l) => Env l -> Data l -> String

View File

@ -30,10 +30,10 @@ setupbuffering = do
hSetBuffering stdout NoBuffering
type Repl' a b = HaskelineT (StateT a IO) b
type Repl a b = HaskelineT (StateT a IO) b
cmd' :: (GenericLambda l) => String -> Repl' (Env l) ()
cmd' input = do
cmd :: (GenericLambda l) => String -> Repl (Env l) ()
cmd input = do
env <- get
(toprint, newenv) <- liftIO $ execLine env input
case toprint of
@ -41,8 +41,8 @@ cmd' input = do
Just e -> liftIO $ putStrLn e
modify (const newenv)
completer' :: (GenericLambda l, Monad m, MonadState (Env l) m) => CompletionFunc m
completer' = \case
completer :: (GenericLambda l, Monad m, MonadState (Env l) m) => CompletionFunc m
completer = \case
("", r) -> do
matches <- gets (map convertmatch . envStrings)
return ("", matches)
@ -63,23 +63,20 @@ completer' = \case
opts = []
-- ini :: Repl ()
-- ini = liftIO $ putStrLn "Type your lambda expression."
ini' :: (GenericLambda l) => Repl' (Env l) ()
ini' = liftIO $ putStrLn "Type your lambda expression."
ini :: (GenericLambda l) => Repl (Env l) ()
ini = liftIO $ putStrLn "Type your lambda expression."
final :: (GenericLambda l) => Repl' (Env l) ExitDecision
final :: (GenericLambda l) => Repl (Env l) ExitDecision
final = do
liftIO $ putStrLn "Goodbye!"
return Exit
repl' :: (GenericLambda l) => IO ((), Env l)
repl' = (runStateT $ evalRepl (const $ pure ">>> ") cmd' opts (Just ':') (Just "paste") (Custom completer') ini' final) emptyEnv
repl :: (GenericLambda l) => IO ((), Env l)
repl = (runStateT $ evalRepl (const $ pure ">>> ") cmd opts (Just ':') (Just "paste") (Custom completer) ini final) emptyEnv
uLrepl :: IO ((), Env UntypedLambda)
uLrepl = repl'
uLrepl = repl

70
src/Typed-0/TLispAst.hs Normal file
View File

@ -0,0 +1,70 @@
{-# LANGUAGE GADTs #-}
module TLispAst where
import qualified Data.Map as M
data Pos a = P {filename :: String, row:: Int, col:: Int, obj:: a}
data Literal = LInt Int | Nil | LChar Char | LString String | LBool Bool
deriving (Eq, Ord)
instance Show Literal where
show (LInt i) = show i
show Nil = "nil"
show (LChar c) = show c
show (LString s) = s
show (LBool b) = show b
newtype Id = Id String
instance Show Id where
show (Id s) = s
-- data Ast where
-- SExp :: Id -> [Ast] -> Ast
-- SLit :: Literal -> Ast
--
newtype Environment = Env (M.Map Id Ast)
data Effect = EPrint String | EError String
data State = S [Effect] Environment
addeff :: State -> Effect -> State
addeff (S es en) e = S (e:es) en
instance Semigroup State where
(<>) (S es0 env0) (S es1 env1) = S (es0 ++ es1) env1
-- eval :: Environment -> Ast -> ([Effect], Environment, Ast)
-- eval en (SLit a) = ([], en, SLit a)
-- eval en (SExp (Id "print") )
data Ast where
Car :: Ast -> Ast
Cdr :: Ast -> Ast
Cons :: Ast -> Ast -> Ast
Print :: Ast -> Ast
Do :: [Ast] -> Ast
Lit :: Literal -> Ast
deriving (Eq, Show)
eval :: State -> Ast -> (State, Ast)
eval s (Car a) = case eval s a of
(s1, Cons b c) -> (s <> s1, b)
(s1, p) -> (addeff s1 (EError ("Cannot call Car on " ++ show p)), Lit Nil)
eval s (Cdr a) = case eval s a of
(s1, Cons b c) -> (s <> s1, c)
(s1, p) -> (addeff s1 (EError ("Cannot call Cdr on " ++ show p)), Lit Nil)
eval s (Print a) = let (s1, k) = eval s a in
(addeff s1 (EPrint (show k)), Lit Nil)
eval s (Cons a b) = (s, Cons a b)
eval s x = (s, x)

1
src/Typed-0/TLispExec.hs Normal file
View File

@ -0,0 +1 @@
module TLispExec where

View File

@ -0,0 +1 @@
module TLispParse where

269
src/Typed-1/LispyAst.hs Normal file
View File

@ -0,0 +1,269 @@
{-# LANGUAGE GADTs #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
module LispyAst where
import qualified Data.Map as M
-- -- we begin representing some primitive types.
data LType = TFloat
| TInt
| TChar
| TBool
| TCons LType LType
| TNil
| TFun [LType] LType
deriving (Eq, Ord, Show)
-- -- now, we make some wrappers
newtype Id = Id String
deriving (Eq, Ord)
instance Show Id where
show (Id s) = s
-- instance GEq (TypedId) where
-- geq (TId i1 t1) (TId i2 t2) = Just ()
-- deriveGEq ''TypedId
-- deriveGCompare ''TypedId
-- deriveArgDict ''TypedId
data Value (a :: LType) where
LFloat :: Float -> Value TFloat
-- LInt :: Integer -> Value TInt
LChar :: Char -> Value TChar
LBool :: Bool -> Value TBool
LCons :: Value b -> Value c -> Value (TCons b c)
Nil :: Value TNil
LFun :: [Id] -> Value x -> Value (TFun y x)
-- LVar :: Id -> Value (ToInfer a)
-- deriving Show
-- LCar :: (Value (TCons a b)) -> Value a
-- LCdr :: (Value (TCons a b)) -> Value b
-- there's probably a better way to do this
-- I mean, there is probably a better way to do this entire mess
-- but it's type safe, ain't that cool
-- instance Eq (Value TFloat) where
-- (==) (LFloat f) (LFloat g) = f == g
--
-- instance Eq (Value TInt) where
-- (==) (LInt a) (LInt b) = a == b
--
instance Num (Value TFloat) where
(+) (LFloat a) (LFloat b) = LFloat $ a + b
(*) (LFloat a) (LFloat b) = LFloat $ a * b
abs (LFloat a) = LFloat $ abs a
instance Show (Value a) where
show (LFloat f) = show f
-- show (LInt i) = show i
show (LChar c) = show c
show (LCons a b) = "(" ++ show a ++ "," ++ show b ++ ")"
show (LBool b) = show b
show Nil = "()"
show (LFun ids body) = "function: " ++ unwords (map show ids) ++ "."
-- show (LVar (Id a)) = a
instance Eq (Value TFloat) where
(==) (LFloat f) (LFloat g) = f == g
--
instance Ord (Value TFloat) where
(>=) (LFloat f) (LFloat g) = f >= g
-- (<)
(<=) (LFloat f) (LFloat g) = f >= g
data Effect = EPrint String | EError String{- | AddEnv Id [Id] SExp | Lookup Id -}
deriving Show
data Ast a where
IdOp :: Value a -> Ast a
Exit :: Ast a
Do :: [WA] -> Ast a -> Ast a
Print :: (Show (Value v)) => Ast v -> Ast TNil
Cons :: Ast a -> Ast b -> Ast (TCons a b)
Car :: Ast (TCons x y) -> Ast x
Cdr :: Ast (TCons x y) -> Ast y
-- Lambda :: [Id] -> Value x -> Ast (TFun y x)
-- Def :: Id -> [Id] -> SExp -> Ast TNil
DEQ :: Value TFloat -> Value TFloat -> Ast TBool
DLT :: Value TFloat -> Value TFloat -> Ast TBool
DGT :: Value TFloat -> Value TFloat -> Ast TBool
Not :: Ast TBool -> Ast TBool
Sum :: Ast TFloat -> Ast TFloat -> Ast TFloat
Sub :: Ast TFloat -> Ast TFloat -> Ast TFloat
-- Div :: (Fractional (Value b)) => Ast b -> Ast b -> Ast b
Mul :: Ast TFloat -> Ast TFloat -> Ast TFloat
Abs :: Ast TFloat -> Ast TFloat
--
deriving instance Show (Value a) => Show (Ast a)
-- deriving instance Num (Value a) => Num (Ast a)
--
interpretdef :: Ast a -> ([Effect], Ast a)
interpretdef (Print a) = ([EPrint (show a)], IdOp Nil)
--
interpretdef (Car (Cons a b)) = ([], a)
interpretdef (Car a) = let (e1, r1) = interpretdef a
(e2, r2) = interpretdef (Car r1)
in (e1 ++ e2, r2)
interpretdef (Cdr (Cons a b)) = ([], b)
interpretdef (Cdr a) = let (e1, r1) = interpretdef a
(e2, r2) = interpretdef (Cdr r1)
in (e1 ++ e2, r2)
interpretdef (Cons a b) = ([],Cons a b)
--
-- interpretdef en (Def id vars body) = ([],en, IdOp Nil)
-- interpretdef en (Lambda vars body) = ([],en, IdOp $ LFun vars body)
--
interpretdef (DEQ x y) = ([], IdOp $ LBool (x == y))
interpretdef (DLT x y) = ([], IdOp $ LBool (x < y))
interpretdef (DGT x y) = ([], IdOp $ LBool (x > y))
--
interpretdef (Not (IdOp (LBool b))) = ([], IdOp $ LBool (not b))
interpretdef (Not a) = let (e1, r1) = interpretdef a
(e2, r2) = interpretdef (Not r1)
in (e1 ++ e2, r2)
--
interpretdef (Sum (IdOp x) (IdOp y)) = ([], IdOp $ x + y)
interpretdef (Sum a b) = let (e1, r1) = interpretdef a
(e2, r2) = interpretdef b
(e3, r3) = interpretdef (Sum r1 r2)
in (e1 ++ e2, r3)
interpretdef (Sub (IdOp x) (IdOp y)) = ([], IdOp $ x - y)
interpretdef (Sub a b) = let (e1, r1) = interpretdef a
(e2, r2) = interpretdef b
(e3, r3) = interpretdef (Sub r1 r2)
in (e1 ++ e2 ++ e3, r3)
-- interpretdef en (Div x y) = (Nothing, x / y)
interpretdef (Mul (IdOp x) (IdOp y)) = ([], IdOp $ x * y)
interpretdef (Mul a b) = let (e1, r1) = interpretdef a
(e2, r2) = interpretdef b
(e3, r3) = interpretdef (Mul r1 r2)
in (e1 ++ e2 ++ e3, r3)
interpretdef (Abs (IdOp x)) = ([], IdOp $ abs x)
interpretdef (Abs a) = let (e1, r1) = interpretdef a
(e2, r2) = interpretdef (Abs r1)
in (e1 ++ e2, r2)
interpretdef (IdOp x) = ([], IdOp x)
interpretdef Exit = ([], Exit)
interpretdef (Do [] end) = ([], end)
interpretdef (Do ((WA x):xs) end) = let (e1, _) = interpretdef x
(e2, r2) = interpretdef (Do xs end)
in (e1 ++ e2, r2)
--
data SExp where
-- Builtin :: Ast (Value a) -> SExp
Literal :: Value a -> SExp
SExp :: Id -> [SExp] -> SExp
--
instance Show SExp where
show (Literal v) = show v
show (SExp id args) = "(" ++ show id ++ " " ++ unwords (map show args) ++ ")"
--
--
-- -- wrapping the value type because existential quantification
-- data WR = forall a. WV (Value a) {- | forall a. WD (Ast (Value a)) -}
data WA = forall a. WA (Ast a)
deriving instance Show WA
-- data WV = forall (a :: LType). WV (Value a)
--
--
--
-- -- convert :: Environment -> SExp -> Maybe WValue
-- -- convert en (Literal v) = Just $ WV v
-- -- convert en
--
--
data Environment = Env (M.Map Id SExp) Environment | Empty
--
instance Show Environment where
show _ = "env."
--
-- lookupEnv :: Environment -> TypedId t -> Maybe (Value t)
-- -- lookupEnv = undefined
-- lookupEnv (Env m parent) id = case M.lookup id m of
-- Nothing -> lookupEnv parent id
-- Just (v) -> Just v
lookupEnv :: Environment -> Id -> Maybe SExp
lookupEnv (Env env parent) id = case M.lookup id env of
Nothing -> lookupEnv parent id
Just s -> Just s
lookupEnv Empty _ = Nothing
--
parseSexp :: Environment -> SExp -> Either String (Environment, WA)
parseSexp en (Literal v) = Right (en, WA (IdOp v))
parseSexp en (SExp (Id "car") [a]) = case parseSexp en a of
Right (en', WA v@(Cons _ _)) -> Right (en', WA (Car v))
_ -> Left "Car can only be applied to a cons cell."
parseSexp en (SExp (Id "cdr") [a]) = case parseSexp en a of
Right (en', WA v@(Cons _ _)) -> Right (en', WA (Cdr v))
_ -> Left "Cdr can only be applied to a cons cell."
parseSexp en (SExp (Id "cons") [a, b]) = case parseSexp en a of
Right (en', WA v1) -> case parseSexp en' b of
Right (en'', WA v2) -> Right (en'', WA (Cons v1 v2))
Left e -> Left e
Left e -> Left e
-- parseSexp en (Sexp (Id "print") xs) = case
parseSexp en (SExp (Id "+") [a, b]) = case parseSexp en a of
Right (en', WA v1@(IdOp (LFloat _))) -> case parseSexp en' b of
Right (en'', WA v2@(IdOp (LFloat _))) -> Right (en'', WA (Mul v1 v2))
Right _ -> Left "Argument to + is not a float"
Left e -> Left e
Right _ -> Left "Argument to + is not a float"
Left e -> Left e
-- parseSexp en (SExp (Id "+") (x:xs)) = case parseSexp en x of
-- Right (en', WA v1@(IdOp (LInt _))) -> case parseSexp en' (SExp (Id "+") xs) of
-- Right (en'', )
-- Right _ -> Left "Argument to + is not numeric"
-- Left e -> Left e
parseSexp _ (SExp x _) = Left $ "Wrong number of arguments to " ++ show x
--
--
--
--
-- -- interpret :: WR -> WV
-- -- interpret (WD a) = let (res, v) = interpretdef en a in interpret (WV v)
-- -- interpret (WV v) = WVF v
-- -- parseSexp en (SExp id args) =
--
--
--
--

22
src/Typed-1/LispyExec.hs Normal file
View File

@ -0,0 +1,22 @@
module LispyExec where
import LispyParser
import LispyAst
test :: String -> IO ()
test s = case runparse s of
Left s -> putStrLn s
Right sexp -> do
putStrLn $ "got a sexp:" ++ show sexp
case parseSexp Empty sexp of
Left e -> putStrLn e
Right (en, v) -> do
-- --
case v of
WA v -> putStrLn $ "Got a value. here: " ++ show (interpretdef v)
--
--
-- putStrLn $ interpret v

View File

@ -0,0 +1,52 @@
{-# LANGUAGE OverloadedStrings #-}
module LispyParser where
import Data.Attoparsec.Text
import LispyAst
import Data.String (fromString)
import Data.Text (unpack)
sexpparser :: Parser SExp
sexpparser = choice [literalparser, parensparser]
identparser :: Parser Id
identparser = Id <$> many1
(letter <>
char '+' <>
char '-' <>
char '*' <>
char '_' <>
char '/')
literalparser = choice [nilparser, falseparser, trueparser, numberparser]
nilparser = do
string "nil" <> string "()"
return (Literal Nil)
falseparser = do
string "false"
return (Literal $ LBool False)
trueparser = do
string "true"
return (Literal $ LBool True)
numberparser = Literal . LFloat . fromIntegral <$> decimal
parensparser :: Parser SExp
parensparser = do
char '('
many' space
id <- identparser
many' space
sexps <- sepBy1' sexpparser (many1' space)
char ')'
return $ SExp id sexps
runparse = parseOnly sexpparser . fromString