Definition dependency analysis
tricu now allows defining terms in any order and will resolve dependencies to ensure that they're evaluated in the right order. Undefined terms are detected and throw errors during dependency ordering. For now we can't define top-level mutually recursive terms.
This commit is contained in:
parent
e2621bc09d
commit
a64b3f0829
@ -1,11 +1,9 @@
|
||||
-- Level Order Traversal of a labelled binary tree
|
||||
-- Objective: Print each "level" of the tree on a separate line
|
||||
--
|
||||
-- NOTICE: This demo relies on tricu base library functions
|
||||
--
|
||||
-- We model labelled binary trees as sublists where values act as labels. We
|
||||
-- require explicit not?ation of empty nodes. Empty nodes can be represented
|
||||
-- with an empty list, `[]`, which is equivalent to a single node `t`.
|
||||
-- We model labelled binary trees as nested lists where values act as labels. We
|
||||
-- require explicit notation of empty nodes. Empty nodes can be represented
|
||||
-- with an empty list, `[]`, which evaluates to a single node `t`.
|
||||
--
|
||||
-- Example tree inputs:
|
||||
-- [("1") [("2") [("4") t t] t] [("3") [("5") t t] [("6") t t]]]]
|
||||
@ -15,7 +13,6 @@
|
||||
-- 2 3
|
||||
-- / / \
|
||||
-- 4 5 6
|
||||
--
|
||||
|
||||
label = \node : head node
|
||||
|
||||
|
113
src/Eval.hs
113
src/Eval.hs
@ -3,14 +3,14 @@ module Eval where
|
||||
import Parser
|
||||
import Research
|
||||
|
||||
import Data.List (partition)
|
||||
import Data.Map (Map)
|
||||
|
||||
import qualified Data.Map as Map
|
||||
import qualified Data.Set as Set
|
||||
|
||||
evalSingle :: Env -> TricuAST -> Env
|
||||
evalSingle env term
|
||||
| SFunc name [] body <- term =
|
||||
| SDef name [] body <- term =
|
||||
if
|
||||
| Map.member name env ->
|
||||
errorWithoutStackTrace $
|
||||
@ -23,18 +23,23 @@ evalSingle env term
|
||||
in Map.insert "__result" res env
|
||||
| SVar name <- term =
|
||||
case Map.lookup name env of
|
||||
Just v -> Map.insert "__result" v env
|
||||
Nothing -> errorWithoutStackTrace $ "Variable `" ++ name ++ "` not defined"
|
||||
Just v ->
|
||||
Map.insert "__result" v env
|
||||
Nothing ->
|
||||
errorWithoutStackTrace $ "Variable `" ++ name ++ "` not defined\n\
|
||||
\This error should never occur here. Please report this as an issue."
|
||||
| otherwise =
|
||||
Map.insert "__result" (evalAST env term) env
|
||||
|
||||
evalTricu :: Env -> [TricuAST] -> Env
|
||||
evalTricu env [] = env
|
||||
evalTricu env [x] =
|
||||
let updatedEnv = evalSingle env x
|
||||
in Map.insert "__result" (result updatedEnv) updatedEnv
|
||||
evalTricu env (x:xs) =
|
||||
evalTricu (evalSingle env x) xs
|
||||
evalTricu env x = go env (reorderDefs env x)
|
||||
where
|
||||
go env [] = env
|
||||
go env [x] =
|
||||
let updatedEnv = evalSingle env x
|
||||
in Map.insert "__result" (result updatedEnv) updatedEnv
|
||||
go env (x:xs) =
|
||||
evalTricu (evalSingle env x) xs
|
||||
|
||||
evalAST :: Env -> TricuAST -> T
|
||||
evalAST env term
|
||||
@ -94,19 +99,83 @@ elimLambda = go
|
||||
_TRIAGE = parseSingle "t (t (t t (t (t (t t t))))) t"
|
||||
_COMPOSE = parseSingle "t (t (t t (t (t (t t t)) t))) (t t)"
|
||||
|
||||
isFree x = Set.member x . freeVars
|
||||
freeVars (SVar v ) = Set.singleton v
|
||||
freeVars (SInt _ ) = Set.empty
|
||||
freeVars (SStr _ ) = Set.empty
|
||||
freeVars (SList s ) = foldMap freeVars s
|
||||
freeVars (SApp f a ) = freeVars f <> freeVars a
|
||||
freeVars (TLeaf ) = Set.empty
|
||||
freeVars (SFunc _ _ b) = freeVars b
|
||||
freeVars (TStem t ) = freeVars t
|
||||
freeVars (TFork l r ) = freeVars l <> freeVars r
|
||||
freeVars (SLambda v b ) = foldr Set.delete (freeVars b) v
|
||||
isFree :: String -> TricuAST -> Bool
|
||||
isFree x = Set.member x . freeVars
|
||||
|
||||
freeVars :: TricuAST -> Set.Set String
|
||||
freeVars (SVar v ) = Set.singleton v
|
||||
freeVars (SInt _ ) = Set.empty
|
||||
freeVars (SStr _ ) = Set.empty
|
||||
freeVars (SList s ) = foldMap freeVars s
|
||||
freeVars (SApp f a ) = freeVars f <> freeVars a
|
||||
freeVars (TLeaf ) = Set.empty
|
||||
freeVars (SDef _ _ b) = freeVars b
|
||||
freeVars (TStem t ) = freeVars t
|
||||
freeVars (TFork l r ) = freeVars l <> freeVars r
|
||||
freeVars (SLambda v b ) = foldr Set.delete (freeVars b) v
|
||||
|
||||
reorderDefs :: Env -> [TricuAST] -> [TricuAST]
|
||||
reorderDefs env defs
|
||||
| not (null missingDeps) =
|
||||
errorWithoutStackTrace $
|
||||
"Missing dependencies detected: " ++ show missingDeps
|
||||
| otherwise = orderedDefs ++ others
|
||||
where
|
||||
(defsOnly, others) = partition isDef defs
|
||||
graph = buildDepGraph defsOnly
|
||||
sortedDefs = sortDeps graph
|
||||
defMap = Map.fromList [(name, def) | def@(SDef name _ _) <- defsOnly]
|
||||
orderedDefs = map (\name -> defMap Map.! name) sortedDefs
|
||||
topDefNames = Set.fromList (Map.keys defMap)
|
||||
envNames = Set.fromList (Map.keys env)
|
||||
freeVarsDefs = foldMap (\(SDef _ _ body) -> freeVars body) defsOnly
|
||||
freeVarsOthers = foldMap freeVars others
|
||||
allFreeVars = freeVarsDefs <> freeVarsOthers
|
||||
validNames = topDefNames `Set.union` envNames
|
||||
missingDeps = Set.toList (allFreeVars `Set.difference` validNames)
|
||||
|
||||
isDef (SDef _ _ _) = True
|
||||
isDef _ = False
|
||||
|
||||
buildDepGraph :: [TricuAST] -> Map.Map String (Set.Set String)
|
||||
buildDepGraph topDefs
|
||||
| not (null duplicateNames) =
|
||||
errorWithoutStackTrace $
|
||||
"Duplicate definitions detected: " ++ show duplicateNames
|
||||
| otherwise =
|
||||
Map.fromList
|
||||
[ (name, depends topDefs (SDef name [] body))
|
||||
| SDef name _ body <- topDefs]
|
||||
where
|
||||
names = [name | SDef name _ _ <- topDefs]
|
||||
duplicateNames =
|
||||
[ name | (name, count) <- Map.toList (countOccurrences names) , count > 1]
|
||||
countOccurrences = foldr (\x -> Map.insertWith (+) x 1) Map.empty
|
||||
|
||||
sortDeps :: Map.Map String (Set.Set String) -> [String]
|
||||
sortDeps graph = go [] (Map.keys graph)
|
||||
where
|
||||
go sorted [] = sorted
|
||||
go sorted remaining
|
||||
| null ready =
|
||||
errorWithoutStackTrace
|
||||
"ERROR: Top-level cyclic dependency detected and prohibited\n\
|
||||
\RESOLVE: Use nested lambdas"
|
||||
| otherwise = go (sorted ++ ready) notReady
|
||||
where
|
||||
ready = [ name | name <- remaining
|
||||
, all (`elem` sorted) (Set.toList (graph Map.! name))]
|
||||
notReady =
|
||||
[ name | name <- remaining , name `notElem` ready]
|
||||
|
||||
depends :: [TricuAST] -> TricuAST -> Set.Set String
|
||||
depends topDefs (SDef _ _ body) =
|
||||
Set.intersection
|
||||
(Set.fromList [n | SDef n _ _ <- topDefs])
|
||||
(freeVars body)
|
||||
depends _ _ = Set.empty
|
||||
|
||||
result :: Env -> T
|
||||
result r = case Map.lookup "__result" r of
|
||||
Just a -> a
|
||||
Nothing -> errorWithoutStackTrace "No __result field found in provided environment"
|
||||
Nothing -> errorWithoutStackTrace "No __result field found in provided env"
|
||||
|
@ -112,7 +112,7 @@ parseFunctionM = do
|
||||
_ <- satisfyM (== LAssign)
|
||||
scnParserM
|
||||
body <- parseExpressionM
|
||||
pure (SFunc name (map getIdentifier args) body)
|
||||
pure (SDef name (map getIdentifier args) body)
|
||||
|
||||
parseLambdaM :: ParserM TricuAST
|
||||
parseLambdaM = do
|
||||
|
@ -19,7 +19,7 @@ data TricuAST
|
||||
| SInt Int
|
||||
| SStr String
|
||||
| SList [TricuAST]
|
||||
| SFunc String [String] TricuAST
|
||||
| SDef String [String] TricuAST
|
||||
| SApp TricuAST TricuAST
|
||||
| TLeaf
|
||||
| TStem TricuAST
|
||||
|
10
test/Spec.hs
10
test/Spec.hs
@ -85,7 +85,7 @@ parser = testGroup "Parser Tests"
|
||||
|
||||
, testCase "Parse function definitions" $ do
|
||||
let input = "x = (\\a b c : a)"
|
||||
expect = SFunc "x" [] (SLambda ["a"] (SLambda ["b"] (SLambda ["c"] (SVar "a"))))
|
||||
expect = SDef "x" [] (SLambda ["a"] (SLambda ["b"] (SLambda ["c"] (SVar "a"))))
|
||||
parseSingle input @?= expect
|
||||
|
||||
, testCase "Parse nested Tree Calculus terms" $ do
|
||||
@ -105,7 +105,7 @@ parser = testGroup "Parser Tests"
|
||||
|
||||
, testCase "Parse function with applications" $ do
|
||||
let input = "f = (\\x : t x)"
|
||||
expect = SFunc "f" [] (SLambda ["x"] (SApp TLeaf (SVar "x")))
|
||||
expect = SDef "f" [] (SLambda ["x"] (SApp TLeaf (SVar "x")))
|
||||
parseSingle input @?= expect
|
||||
|
||||
, testCase "Parse nested lists" $ do
|
||||
@ -147,7 +147,7 @@ parser = testGroup "Parser Tests"
|
||||
|
||||
, testCase "Parse nested parentheses in function body" $ do
|
||||
let input = "f = (\\x : t (t (t t)))"
|
||||
expect = SFunc "f" [] (SLambda ["x"] (SApp TLeaf (SApp TLeaf (SApp TLeaf TLeaf))))
|
||||
expect = SDef "f" [] (SLambda ["x"] (SApp TLeaf (SApp TLeaf (SApp TLeaf TLeaf))))
|
||||
parseSingle input @?= expect
|
||||
|
||||
, testCase "Parse lambda abstractions" $ do
|
||||
@ -157,12 +157,12 @@ parser = testGroup "Parser Tests"
|
||||
|
||||
, testCase "Parse multiple arguments to lambda abstractions" $ do
|
||||
let input = "x = (\\a b : a)"
|
||||
expect = SFunc "x" [] (SLambda ["a"] (SLambda ["b"] (SVar "a")))
|
||||
expect = SDef "x" [] (SLambda ["a"] (SLambda ["b"] (SVar "a")))
|
||||
parseSingle input @?= expect
|
||||
|
||||
, testCase "Grouping T terms with parentheses in function application" $ do
|
||||
let input = "x = (\\a : a)\nx (t)"
|
||||
expect = [SFunc "x" [] (SLambda ["a"] (SVar "a")),SApp (SVar "x") TLeaf]
|
||||
expect = [SDef "x" [] (SLambda ["a"] (SVar "a")),SApp (SVar "x") TLeaf]
|
||||
parseTricu input @?= expect
|
||||
|
||||
, testCase "Comments 1" $ do
|
||||
|
21
test/size.tri
Normal file
21
test/size.tri
Normal file
@ -0,0 +1,21 @@
|
||||
compose = \f g x : f (g x)
|
||||
|
||||
succ = y (\self :
|
||||
triage
|
||||
1
|
||||
t
|
||||
(triage
|
||||
(t (t t))
|
||||
(\_ tail : t t (self tail))
|
||||
t))
|
||||
|
||||
size = (\x :
|
||||
(y (\self x :
|
||||
compose succ
|
||||
(triage
|
||||
(\x : x)
|
||||
self
|
||||
(\x y : compose (self x) (self y))
|
||||
x)) x 0))
|
||||
|
||||
size size
|
1
test/undefined.tri
Normal file
1
test/undefined.tri
Normal file
@ -0,0 +1 @@
|
||||
namedTerm = undefinedForTesting
|
@ -1,7 +1,7 @@
|
||||
cabal-version: 1.12
|
||||
|
||||
name: tricu
|
||||
version: 0.9.0
|
||||
version: 0.10.0
|
||||
description: A micro-language for exploring Tree Calculus
|
||||
author: James Eversole
|
||||
maintainer: james@eversole.co
|
||||
|
Loading…
x
Reference in New Issue
Block a user