Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
a64b3f0829 |
@ -1,11 +1,9 @@
|
|||||||
-- Level Order Traversal of a labelled binary tree
|
-- Level Order Traversal of a labelled binary tree
|
||||||
-- Objective: Print each "level" of the tree on a separate line
|
-- 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 nested lists where values act as labels. We
|
||||||
--
|
-- require explicit notation of empty nodes. Empty nodes can be represented
|
||||||
-- We model labelled binary trees as sublists where values act as labels. We
|
-- with an empty list, `[]`, which evaluates to a single node `t`.
|
||||||
-- require explicit not?ation of empty nodes. Empty nodes can be represented
|
|
||||||
-- with an empty list, `[]`, which is equivalent to a single node `t`.
|
|
||||||
--
|
--
|
||||||
-- Example tree inputs:
|
-- Example tree inputs:
|
||||||
-- [("1") [("2") [("4") t t] t] [("3") [("5") t t] [("6") t t]]]]
|
-- [("1") [("2") [("4") t t] t] [("3") [("5") t t] [("6") t t]]]]
|
||||||
@ -15,7 +13,6 @@
|
|||||||
-- 2 3
|
-- 2 3
|
||||||
-- / / \
|
-- / / \
|
||||||
-- 4 5 6
|
-- 4 5 6
|
||||||
--
|
|
||||||
|
|
||||||
label = \node : head node
|
label = \node : head node
|
||||||
|
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
compose = \f g x : f (g x)
|
compose = \f g x : f (g x)
|
||||||
|
|
||||||
succ = y (\self :
|
succ = y (\self :
|
||||||
triage
|
triage
|
||||||
1
|
1
|
||||||
t
|
t
|
||||||
(triage
|
(triage
|
||||||
(t (t t))
|
(t (t t))
|
||||||
(\_ tail : t t (self tail))
|
(\_ tail : t t (self tail))
|
||||||
t))
|
t))
|
||||||
|
|
||||||
size = (\x :
|
size = (\x :
|
||||||
(y (\self x :
|
(y (\self x :
|
||||||
compose succ
|
compose succ
|
||||||
(triage
|
(triage
|
||||||
(\x : x)
|
(\x : x)
|
||||||
self
|
self
|
||||||
(\x y : compose (self x) (self y))
|
(\x y : compose (self x) (self y))
|
||||||
x)) x 0))
|
x)) x 0))
|
||||||
|
|
||||||
size size
|
size size
|
||||||
|
60
lib/base.tri
60
lib/base.tri
@ -18,9 +18,9 @@ y = ((\mut wait fun : wait mut (\x : fun (wait mut x)))
|
|||||||
triage = \leaf stem fork : t (t leaf stem) fork
|
triage = \leaf stem fork : t (t leaf stem) fork
|
||||||
test = triage "Leaf" (\_ : "Stem") (\_ _ : "Fork")
|
test = triage "Leaf" (\_ : "Stem") (\_ _ : "Fork")
|
||||||
|
|
||||||
matchBool = (\ot of : triage
|
matchBool = (\ot of : triage
|
||||||
of
|
of
|
||||||
(\_ : ot)
|
(\_ : ot)
|
||||||
(\_ _ : ot)
|
(\_ _ : ot)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -35,44 +35,44 @@ emptyList? = matchList true (\_ _ : false)
|
|||||||
head = matchList t (\head _ : head)
|
head = matchList t (\head _ : head)
|
||||||
tail = matchList t (\_ tail : tail)
|
tail = matchList t (\_ tail : tail)
|
||||||
|
|
||||||
lconcat = y (\self : matchList
|
lconcat = y (\self : matchList
|
||||||
(\k : k)
|
(\k : k)
|
||||||
(\h r k : pair h (self r k)))
|
(\h r k : pair h (self r k)))
|
||||||
|
|
||||||
lAnd = (triage
|
lAnd = (triage
|
||||||
(\_ : false)
|
(\_ : false)
|
||||||
(\_ x : x)
|
(\_ x : x)
|
||||||
(\_ _ x : x))
|
(\_ _ x : x))
|
||||||
|
|
||||||
lOr = (triage
|
lOr = (triage
|
||||||
(\x : x)
|
(\x : x)
|
||||||
(\_ _ : true)
|
(\_ _ : true)
|
||||||
(\_ _ _ : true))
|
(\_ _ _ : true))
|
||||||
|
|
||||||
map_ = y (\self :
|
map_ = y (\self :
|
||||||
matchList
|
matchList
|
||||||
(\_ : t)
|
(\_ : t)
|
||||||
(\head tail f : pair (f head) (self tail f)))
|
(\head tail f : pair (f head) (self tail f)))
|
||||||
map = \f l : map_ l f
|
map = \f l : map_ l f
|
||||||
|
|
||||||
equal? = y (\self : triage
|
equal? = y (\self : triage
|
||||||
(triage
|
(triage
|
||||||
true
|
true
|
||||||
(\_ : false)
|
(\_ : false)
|
||||||
(\_ _ : false))
|
(\_ _ : false))
|
||||||
(\ax :
|
(\ax :
|
||||||
triage
|
triage
|
||||||
false
|
false
|
||||||
(self ax)
|
(self ax)
|
||||||
(\_ _ : false))
|
(\_ _ : false))
|
||||||
(\ax ay :
|
(\ax ay :
|
||||||
triage
|
triage
|
||||||
false
|
false
|
||||||
(\_ : false)
|
(\_ : false)
|
||||||
(\bx by : lAnd (self ax bx) (self ay by))))
|
(\bx by : lAnd (self ax bx) (self ay by))))
|
||||||
|
|
||||||
filter_ = y (\self : matchList
|
filter_ = y (\self : matchList
|
||||||
(\_ : t)
|
(\_ : t)
|
||||||
(\head tail f : matchBool (t head) i (f head) (self tail f)))
|
(\head tail f : matchBool (t head) i (f head) (self tail f)))
|
||||||
filter = \f l : filter_ l f
|
filter = \f l : filter_ l f
|
||||||
|
|
||||||
|
125
src/Eval.hs
125
src/Eval.hs
@ -3,19 +3,19 @@ module Eval where
|
|||||||
import Parser
|
import Parser
|
||||||
import Research
|
import Research
|
||||||
|
|
||||||
|
import Data.List (partition)
|
||||||
import Data.Map (Map)
|
import Data.Map (Map)
|
||||||
|
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
|
|
||||||
evalSingle :: Env -> TricuAST -> Env
|
evalSingle :: Env -> TricuAST -> Env
|
||||||
evalSingle env term
|
evalSingle env term
|
||||||
| SFunc name [] body <- term =
|
| SDef name [] body <- term =
|
||||||
if
|
if
|
||||||
| Map.member name env ->
|
| Map.member name env ->
|
||||||
errorWithoutStackTrace $
|
errorWithoutStackTrace $
|
||||||
"Error: Identifier '" ++ name ++ "' is already defined."
|
"Error: Identifier '" ++ name ++ "' is already defined."
|
||||||
| otherwise ->
|
| otherwise ->
|
||||||
let res = evalAST env body
|
let res = evalAST env body
|
||||||
in Map.insert "__result" res (Map.insert name res env)
|
in Map.insert "__result" res (Map.insert name res env)
|
||||||
| SApp func arg <- term =
|
| SApp func arg <- term =
|
||||||
@ -23,18 +23,23 @@ evalSingle env term
|
|||||||
in Map.insert "__result" res env
|
in Map.insert "__result" res env
|
||||||
| SVar name <- term =
|
| SVar name <- term =
|
||||||
case Map.lookup name env of
|
case Map.lookup name env of
|
||||||
Just v -> Map.insert "__result" v env
|
Just v ->
|
||||||
Nothing -> errorWithoutStackTrace $ "Variable `" ++ name ++ "` not defined"
|
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 =
|
| otherwise =
|
||||||
Map.insert "__result" (evalAST env term) env
|
Map.insert "__result" (evalAST env term) env
|
||||||
|
|
||||||
evalTricu :: Env -> [TricuAST] -> Env
|
evalTricu :: Env -> [TricuAST] -> Env
|
||||||
evalTricu env [] = env
|
evalTricu env x = go env (reorderDefs env x)
|
||||||
evalTricu env [x] =
|
where
|
||||||
let updatedEnv = evalSingle env x
|
go env [] = env
|
||||||
in Map.insert "__result" (result updatedEnv) updatedEnv
|
go env [x] =
|
||||||
evalTricu env (x:xs) =
|
let updatedEnv = evalSingle env x
|
||||||
evalTricu (evalSingle env x) xs
|
in Map.insert "__result" (result updatedEnv) updatedEnv
|
||||||
|
go env (x:xs) =
|
||||||
|
evalTricu (evalSingle env x) xs
|
||||||
|
|
||||||
evalAST :: Env -> TricuAST -> T
|
evalAST :: Env -> TricuAST -> T
|
||||||
evalAST env term
|
evalAST env term
|
||||||
@ -61,13 +66,13 @@ elimLambda = go
|
|||||||
go (SLambda [v] (SApp f (SVar x)))
|
go (SLambda [v] (SApp f (SVar x)))
|
||||||
| v == x && not (isFree v f) = elimLambda f
|
| v == x && not (isFree v f) = elimLambda f
|
||||||
-- Triage optimization
|
-- Triage optimization
|
||||||
go (SLambda [a] (SLambda [b] (SLambda [c] body)))
|
go (SLambda [a] (SLambda [b] (SLambda [c] body)))
|
||||||
| body == triageBody = _TRIAGE
|
| body == triageBody = _TRIAGE
|
||||||
where
|
where
|
||||||
triageBody =
|
triageBody =
|
||||||
(SApp (SApp TLeaf (SApp (SApp TLeaf (SVar a)) (SVar b))) (SVar c))
|
(SApp (SApp TLeaf (SApp (SApp TLeaf (SVar a)) (SVar b))) (SVar c))
|
||||||
-- Composition optimization
|
-- Composition optimization
|
||||||
go (SLambda [f] (SLambda [g] (SLambda [x] body)))
|
go (SLambda [f] (SLambda [g] (SLambda [x] body)))
|
||||||
| body == composeBody = _COMPOSE
|
| body == composeBody = _COMPOSE
|
||||||
where
|
where
|
||||||
composeBody = SApp (SVar f) (SApp (SVar g) (SVar x))
|
composeBody = SApp (SVar f) (SApp (SVar g) (SVar x))
|
||||||
@ -93,20 +98,84 @@ elimLambda = go
|
|||||||
_I = parseSingle "t (t (t t)) t"
|
_I = parseSingle "t (t (t t)) t"
|
||||||
_TRIAGE = parseSingle "t (t (t t (t (t (t t t))))) t"
|
_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)"
|
_COMPOSE = parseSingle "t (t (t t (t (t (t t t)) t))) (t t)"
|
||||||
|
|
||||||
isFree x = Set.member x . freeVars
|
isFree :: String -> TricuAST -> Bool
|
||||||
freeVars (SVar v ) = Set.singleton v
|
isFree x = Set.member x . freeVars
|
||||||
freeVars (SInt _ ) = Set.empty
|
|
||||||
freeVars (SStr _ ) = Set.empty
|
freeVars :: TricuAST -> Set.Set String
|
||||||
freeVars (SList s ) = foldMap freeVars s
|
freeVars (SVar v ) = Set.singleton v
|
||||||
freeVars (SApp f a ) = freeVars f <> freeVars a
|
freeVars (SInt _ ) = Set.empty
|
||||||
freeVars (TLeaf ) = Set.empty
|
freeVars (SStr _ ) = Set.empty
|
||||||
freeVars (SFunc _ _ b) = freeVars b
|
freeVars (SList s ) = foldMap freeVars s
|
||||||
freeVars (TStem t ) = freeVars t
|
freeVars (SApp f a ) = freeVars f <> freeVars a
|
||||||
freeVars (TFork l r ) = freeVars l <> freeVars r
|
freeVars (TLeaf ) = Set.empty
|
||||||
freeVars (SLambda v b ) = foldr Set.delete (freeVars b) v
|
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 :: Env -> T
|
||||||
result r = case Map.lookup "__result" r of
|
result r = case Map.lookup "__result" r of
|
||||||
Just a -> a
|
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)
|
_ <- satisfyM (== LAssign)
|
||||||
scnParserM
|
scnParserM
|
||||||
body <- parseExpressionM
|
body <- parseExpressionM
|
||||||
pure (SFunc name (map getIdentifier args) body)
|
pure (SDef name (map getIdentifier args) body)
|
||||||
|
|
||||||
parseLambdaM :: ParserM TricuAST
|
parseLambdaM :: ParserM TricuAST
|
||||||
parseLambdaM = do
|
parseLambdaM = do
|
||||||
|
@ -19,7 +19,7 @@ data TricuAST
|
|||||||
| SInt Int
|
| SInt Int
|
||||||
| SStr String
|
| SStr String
|
||||||
| SList [TricuAST]
|
| SList [TricuAST]
|
||||||
| SFunc String [String] TricuAST
|
| SDef String [String] TricuAST
|
||||||
| SApp TricuAST TricuAST
|
| SApp TricuAST TricuAST
|
||||||
| TLeaf
|
| TLeaf
|
||||||
| TStem TricuAST
|
| TStem TricuAST
|
||||||
|
10
test/Spec.hs
10
test/Spec.hs
@ -85,7 +85,7 @@ parser = testGroup "Parser Tests"
|
|||||||
|
|
||||||
, testCase "Parse function definitions" $ do
|
, testCase "Parse function definitions" $ do
|
||||||
let input = "x = (\\a b c : a)"
|
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
|
parseSingle input @?= expect
|
||||||
|
|
||||||
, testCase "Parse nested Tree Calculus terms" $ do
|
, testCase "Parse nested Tree Calculus terms" $ do
|
||||||
@ -105,7 +105,7 @@ parser = testGroup "Parser Tests"
|
|||||||
|
|
||||||
, testCase "Parse function with applications" $ do
|
, testCase "Parse function with applications" $ do
|
||||||
let input = "f = (\\x : t x)"
|
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
|
parseSingle input @?= expect
|
||||||
|
|
||||||
, testCase "Parse nested lists" $ do
|
, testCase "Parse nested lists" $ do
|
||||||
@ -147,7 +147,7 @@ parser = testGroup "Parser Tests"
|
|||||||
|
|
||||||
, testCase "Parse nested parentheses in function body" $ do
|
, testCase "Parse nested parentheses in function body" $ do
|
||||||
let input = "f = (\\x : t (t (t t)))"
|
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
|
parseSingle input @?= expect
|
||||||
|
|
||||||
, testCase "Parse lambda abstractions" $ do
|
, testCase "Parse lambda abstractions" $ do
|
||||||
@ -157,12 +157,12 @@ parser = testGroup "Parser Tests"
|
|||||||
|
|
||||||
, testCase "Parse multiple arguments to lambda abstractions" $ do
|
, testCase "Parse multiple arguments to lambda abstractions" $ do
|
||||||
let input = "x = (\\a b : a)"
|
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
|
parseSingle input @?= expect
|
||||||
|
|
||||||
, testCase "Grouping T terms with parentheses in function application" $ do
|
, testCase "Grouping T terms with parentheses in function application" $ do
|
||||||
let input = "x = (\\a : a)\nx (t)"
|
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
|
parseTricu input @?= expect
|
||||||
|
|
||||||
, testCase "Comments 1" $ do
|
, 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
|
cabal-version: 1.12
|
||||||
|
|
||||||
name: tricu
|
name: tricu
|
||||||
version: 0.9.0
|
version: 0.10.0
|
||||||
description: A micro-language for exploring Tree Calculus
|
description: A micro-language for exploring Tree Calculus
|
||||||
author: James Eversole
|
author: James Eversole
|
||||||
maintainer: james@eversole.co
|
maintainer: james@eversole.co
|
||||||
|
Reference in New Issue
Block a user