Stop using lists to represent args

This commit is contained in:
2024-12-27 08:17:06 -06:00
committed by James Eversole
parent 37be4aac0a
commit 5e574d2ca1
7 changed files with 399 additions and 299 deletions

View File

@ -2,63 +2,139 @@ module Eval where
import Parser
import Research
import Data.Set (Set)
import qualified Data.Set as Set
import Data.List (foldl')
import qualified Data.Map as Map
import Data.Map (Map)
import Data.Map (Map)
evalSingle :: Map String T -> SaplingAST -> Map String T
evalSingle env TLeaf = Map.insert "__result" Leaf env
evalSingle env (TStem t) =
let result = Stem (evalTreeCalculus env t)
in Map.insert "__result" result env
evalSingle env (TFork t1 t2) =
let result = Fork (evalTreeCalculus env t1) (evalTreeCalculus env t2)
in Map.insert "__result" result env
evalSingle env (SFunc name [] body) =
let value = evalTreeCalculus env body
in Map.insert name value env
evalSingle env (SVar name) =
case Map.lookup name env of
Just value -> Map.insert "__result" value env
Nothing -> error $ "Variable " ++ name ++ " not defined"
evalSingle env ast = Map.insert "__result" (evalTreeCalculus env ast) env
evalSingle :: Map.Map String T -> SaplingAST -> Map.Map String T
evalSingle env term = case term of
SFunc name [] body ->
let result = evalAST env body
in Map.insert name result env
SApp func arg ->
let result = apply (evalAST env func) (evalAST env arg)
in Map.insert "__result" result env
SVar name -> case Map.lookup name env of
Just value -> Map.insert "__result" value env
Nothing -> error $ "Variable " ++ name ++ " not defined"
_ ->
let result = evalAST env term
in Map.insert "__result" result env
evalSapling :: Map String T -> [SaplingAST] -> Map String T
evalSapling env [] = env
evalSapling env [lastLine] =
let updatedEnv = evalSingle env lastLine
let
lastLineNoLambda = eliminateLambda lastLine
updatedEnv = evalSingle env lastLineNoLambda
in Map.insert "__result" (result updatedEnv) updatedEnv
evalSapling env (line:rest) =
let updatedEnv = evalSingle env line
let
lineNoLambda = eliminateLambda line
updatedEnv = evalSingle env lineNoLambda
in evalSapling updatedEnv rest
evalTreeCalculus :: Map.Map String T -> SaplingAST -> T
evalTreeCalculus _ TLeaf = Leaf
evalTreeCalculus env (TStem t) = Stem (evalTreeCalculus env t)
evalTreeCalculus env (TFork t1 t2) = Fork (evalTreeCalculus env t1) (evalTreeCalculus env t2)
evalTreeCalculus env (SApp base []) = evalTreeCalculus env base
evalTreeCalculus env (SApp base args) =
let func = evalTreeCalculus env base
argVals = map (evalTreeCalculus env) args
in foldl apply func argVals
evalTreeCalculus env (SVar name) =
case Map.lookup name env of
Just value -> value
Nothing -> error $ "Variable " ++ name ++ " not defined"
evalTreeCalculus _ (SStr str) = toString str
evalTreeCalculus _ (SInt num) = toNumber num
evalTreeCalculus _ (SList elems) = toList (map (evalTreeCalculus Map.empty) elems)
evalTreeCalculus _ (SFunc name args body) =
error $ "Unexpected function definition " ++ name ++ " in \
\ evalTreeCalculus; functions should be evaluated to Tree Calculus \
\ terms by evalSingle."
evalAST :: Map String T -> SaplingAST -> T
evalAST env term = case term of
SVar name ->
case Map.lookup name env of
Just value -> value
Nothing -> error $ "Variable " ++ name ++ " not defined"
TLeaf -> Leaf
TStem t ->
Stem (evalAST env t)
TFork t1 t2 ->
Fork (evalAST env t1) (evalAST env t2)
SApp t1 t2 ->
apply (evalAST env t1) (evalAST env t2)
SStr str -> toString str
SInt num -> toNumber num
SList elems -> toList (map (evalAST Map.empty) elems)
SFunc name args body ->
error $ "Unexpected function definition " ++ name
++ " in evalAST; define via evalSingle."
SLambda {} ->
error "Internal error: SLambda found in evalAST after elimination."
result :: Map String T -> T
result r = case (Map.lookup "__result" r) of
Just a -> a
Nothing -> error "No __result field found in provided environment"
result r = case Map.lookup "__result" r of
Just a -> a
Nothing -> error "No __result field found in provided environment"
eliminateLambda :: SaplingAST -> SaplingAST
eliminateLambda (SLambda (v:vs) body)
| null vs = lambdaToT v (eliminateLambda body)
| otherwise =
eliminateLambda (SLambda [v] (SLambda vs body))
eliminateLambda (SApp f arg) =
SApp (eliminateLambda f) (eliminateLambda arg)
eliminateLambda (TStem t) =
TStem (eliminateLambda t)
eliminateLambda (TFork l r) =
TFork (eliminateLambda l) (eliminateLambda r)
eliminateLambda (SList xs) =
SList (map eliminateLambda xs)
eliminateLambda (SFunc n vs b) =
SFunc n vs (eliminateLambda b)
eliminateLambda other = other
lambdaToT :: String -> SaplingAST -> SaplingAST
lambdaToT x (SVar y)
| x == y = tI
lambdaToT x (SVar y)
| x /= y =
SApp tK (SVar y)
lambdaToT x t
| not (isFree x t) =
SApp tK t
lambdaToT x (SApp n u)
| not (isFree x (SApp n u)) =
SApp tK (SApp (eliminateLambda n) (eliminateLambda u))
lambdaToT x (SApp n u) =
SApp
(SApp tS (lambdaToT x (eliminateLambda n)))
(lambdaToT x (eliminateLambda u))
lambdaToT x (SApp f args) = lambdaToT x f
lambdaToT x body
| not (isFree x body) =
SApp tK body
| otherwise =
SApp
(SApp tS (lambdaToT x body))
tLeaf
tLeaf :: SaplingAST
tLeaf = TLeaf
freeVars :: SaplingAST -> Set String
freeVars (SVar v) = Set.singleton v
freeVars (SInt _) = Set.empty
freeVars (SStr _) = Set.empty
freeVars (SList xs) = foldMap freeVars xs
freeVars (SFunc _ _ b) = freeVars b
freeVars (SApp f arg) = freeVars f <> freeVars arg
freeVars TLeaf = Set.empty
freeVars (TStem t) = freeVars t
freeVars (TFork l r) = freeVars l <> freeVars r
freeVars (SLambda vs b) = foldr Set.delete (freeVars b) vs
isFree :: String -> SaplingAST -> Bool
isFree x = Set.member x . freeVars
toAST :: T -> SaplingAST
toAST Leaf = TLeaf
toAST (Stem a) = TStem (toAST a)
toAST Leaf = TLeaf
toAST (Stem a) = TStem (toAST a)
toAST (Fork a b) = TFork (toAST a) (toAST b)
tI :: SaplingAST
tI = toAST _I
tK :: SaplingAST
tK = toAST _K
tS :: SaplingAST
tS = toAST _S

View File

@ -13,6 +13,8 @@ data LToken
| LIntegerLiteral Int
| LStringLiteral String
| LAssign
| LColon
| LBackslash
| LOpenParen
| LCloseParen
| LOpenBracket
@ -48,6 +50,12 @@ stringLiteral = do
assign :: Lexer LToken
assign = char '=' *> pure LAssign
colon :: Lexer LToken
colon = char ':' *> pure LColon
backslash :: Lexer LToken
backslash = char '\\' *> pure LBackslash
openParen :: Lexer LToken
openParen = char '(' *> pure LOpenParen
@ -73,6 +81,8 @@ saplingLexer = many (sc *> choice
, try integerLiteral
, try stringLiteral
, assign
, colon
, backslash
, openParen
, closeParen
, openBracket

View File

@ -6,7 +6,7 @@ import Parser
import REPL (repl)
import Research
import Data.Map as Map
import qualified Data.Map as Map
import Text.Megaparsec (runParser)
main :: IO ()

View File

@ -1,5 +1,7 @@
module Parser where
import Debug.Trace
import Lexer
import Research hiding (toList)
@ -13,15 +15,16 @@ import Text.Megaparsec.Error (errorBundlePretty, ParseErrorBundle)
type Parser = Parsec Void [LToken]
data SaplingAST
= SVar String
| SInt Int
| SStr String
| SList [SaplingAST]
| SFunc String [String] SaplingAST
| SApp SaplingAST [SaplingAST]
= SVar String
| SInt Int
| SStr String
| SList [SaplingAST]
| SFunc String [String] SaplingAST
| SApp SaplingAST SaplingAST
| TLeaf
| TStem SaplingAST
| TFork SaplingAST SaplingAST
| TStem SaplingAST
| TFork SaplingAST SaplingAST
| SLambda [String] SaplingAST
deriving (Show, Eq, Ord)
parseSapling :: String -> [SaplingAST]
@ -41,10 +44,11 @@ scnParser = skipMany (satisfy isNewline)
parseExpression :: Parser SaplingAST
parseExpression = choice
[ try parseFunction
, try parseLambda
, try parseListLiteral
, try parseApplication
, parseTreeTerm
, try parseTreeTerm
, parseLiteral
, parseListLiteral
]
parseFunction :: Parser SaplingAST
@ -55,24 +59,54 @@ parseFunction = do
body <- parseExpression
return (SFunc name (map getIdentifier args) body)
parseLambda :: Parser SaplingAST
parseLambda = between (satisfy (== LOpenParen)) (satisfy (== LCloseParen)) $ do
satisfy (== LBackslash)
param <- satisfy isIdentifier
rest <- many (satisfy isIdentifier)
satisfy (== LColon)
body <- parseLambdaExpression
let nestedLambda = foldr (\v acc -> SLambda [v] acc) body (map getIdentifier rest)
return (SLambda [getIdentifier param] nestedLambda)
parseLambdaExpression :: Parser SaplingAST
parseLambdaExpression = choice
[ try parseLambdaApplication
, parseAtomicLambda
]
parseAtomicLambda :: Parser SaplingAST
parseAtomicLambda = choice
[ parseVar
, parseTreeLeaf
, parseLiteral
, parseListLiteral
, between (satisfy (== LOpenParen)) (satisfy (== LCloseParen)) parseLambdaExpression
]
parseApplication :: Parser SaplingAST
parseApplication = do
func <- parseAtomicBase
args <- many parseAtomicApplication
case func of
TLeaf | not (null args) && all isTreeTerm args -> fail "Defer to Tree Calculus"
_ -> return (SApp func args)
args <- many parseAtomic
return $ foldl (\acc arg -> SApp acc arg) func args
parseLambdaApplication :: Parser SaplingAST
parseLambdaApplication = do
func <- parseAtomicLambda
args <- many parseAtomicLambda
return $ foldl (\acc arg -> SApp acc arg) func args
isTreeTerm :: SaplingAST -> Bool
isTreeTerm TLeaf = True
isTreeTerm (TStem _) = True
isTreeTerm TLeaf = True
isTreeTerm (TStem _) = True
isTreeTerm (TFork _ _) = True
isTreeTerm _ = False
isTreeTerm _ = False
parseAtomicBase :: Parser SaplingAST
parseAtomicBase = choice
[ parseVar
, parseTreeLeaf
, parseGrouped
]
parseTreeLeaf :: Parser SaplingAST
@ -107,27 +141,15 @@ foldTree (x:y:rest) = TFork x (foldTree (y:rest))
parseAtomic :: Parser SaplingAST
parseAtomic = choice
[ parseVar
, parseTreeLeafOrParenthesized
, parseLiteral
, parseTreeLeaf
, parseListLiteral
, between (satisfy (== LOpenParen)) (satisfy (== LCloseParen)) parseExpression
, parseGrouped
, parseLiteral
]
parseAtomicApplication :: Parser SaplingAST
parseAtomicApplication = do
token <- anySingle
case token of
LAssign -> fail
"Unexpected `=` character in application context. \
\ This is usually caused by an incomplete definition such as: \
\ `function a b =`"
LIdentifier name -> return (SVar name)
LKeywordT -> return TLeaf
LIntegerLiteral value -> return (SInt value)
LStringLiteral value -> return (SStr value)
LOpenBracket -> parseListLiteral
LOpenParen -> between (satisfy (== LOpenParen)) (satisfy (== LCloseParen)) parseExpression
_ -> fail "Invalid token while parsing attempted function application"
parseGrouped :: Parser SaplingAST
parseGrouped = between (satisfy (== LOpenParen)) (satisfy (== LCloseParen)) parseExpression
parseLiteral :: Parser SaplingAST
parseLiteral = choice