Compare commits
No commits in common. "main" and "feat/ternary-representation" have entirely different histories.
main
...
feat/terna
17
.gitignore
vendored
17
.gitignore
vendored
@ -1,11 +1,14 @@
|
|||||||
|
bin/
|
||||||
|
data/Purr.sqlite
|
||||||
|
data/encryptionKey
|
||||||
|
/result
|
||||||
|
/config.dhall
|
||||||
|
/Dockerfile
|
||||||
|
/docker-stack.yml
|
||||||
|
.stack-work/
|
||||||
*.swp
|
*.swp
|
||||||
*.txt
|
dist*
|
||||||
*~
|
*~
|
||||||
.env
|
.env
|
||||||
.stack-work/
|
|
||||||
/Dockerfile
|
|
||||||
/config.dhall
|
|
||||||
/result
|
|
||||||
WD
|
WD
|
||||||
bin/
|
*.hs.txt
|
||||||
dist*
|
|
||||||
|
74
README.md
74
README.md
@ -19,62 +19,42 @@ tricu is the word for "tree" in Lojban: `(x1) is a tree of species/cultivar (x2)
|
|||||||
## What does it look like?
|
## What does it look like?
|
||||||
|
|
||||||
```
|
```
|
||||||
tricu < -- Anything after `--` on a single line is a comment
|
-- Anything after `--` on a single line is a comment
|
||||||
tricu < id = (\a : a) -- Lambda abstraction is eliminated to tree calculus terms
|
-- We can define functions or "variables" as Tree Calculus values
|
||||||
tricu < head (map (\i : lconcat i " world!") [("Hello, ")])
|
false = t
|
||||||
tricu > "Hello, world!"
|
_ = t
|
||||||
tricu < id (head (map (\i : lconcat i " world!") [("Hello, ")]))
|
true = t t
|
||||||
tricu > "Hello, world!"
|
-- We can define functions as lambda expressions that are eliminated to Tree
|
||||||
|
-- Calculus terms.
|
||||||
|
id = (\a : a) -- `id` evaluates to the TC form of: t (t (t t)) t
|
||||||
|
triage = (\a b c : t (t a b) c)
|
||||||
|
-- Intensionality! We can inspect program structure, not just inputs/outputs:
|
||||||
|
test = triage "Leaf" (\_ : "Stem") (\_ _ : "Fork")
|
||||||
|
|
||||||
tricu < -- Intensionality! We can inspect the structure of a function.
|
-- REPL
|
||||||
tricu < triage = (\a b c : t (t a b) c)
|
-- `tricu <` is the input prompt
|
||||||
tricu < test = triage "Leaf" (\z : "Stem") (\a b : "Fork")
|
-- `tricu >` is the Tree Calculus form output. Most are elided below.
|
||||||
tricu < test t t
|
-- `READ -:` is an attempt to interpret the TC output as strings/numbers.
|
||||||
tricu > "Stem"
|
tricu < test t
|
||||||
tricu < -- We can even write a function to convert a function to source code
|
tricu > Fork (Fork Leaf (Fork ...) ... )
|
||||||
tricu < toTString id
|
READ -: "Leaf"
|
||||||
tricu > "t (t (t t)) t"
|
tricu < test (t t)
|
||||||
|
READ -: "Stem"
|
||||||
|
tricu < test (t t t)
|
||||||
|
READ -: "Fork"
|
||||||
|
tricu < map (\i : listConcat i " is super cool!") [("Tree Calculus") ("Intensionality") ("tricu")]
|
||||||
|
READ -: ["Tree Calculus is super cool!", "Intensionality is super cool!", "tricu is super cool!"]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Installation and Use
|
## Installation
|
||||||
|
|
||||||
You can easily build and/or run this project using [Nix](https://nixos.org/download/).
|
You can easily build and/or run this project using [Nix](https://nixos.org/download/).
|
||||||
|
|
||||||
- Quick Start (REPL):
|
- Run REPL immediately:
|
||||||
- `nix run git+https://git.eversole.co/James/tricu`
|
- `nix run git+https://git.eversole.co/James/tricu`
|
||||||
- Build executable in `./result/bin`:
|
- Build REPL executable in `./result/bin`:
|
||||||
- `nix build git+https://git.eversole.co/James/tricu`
|
- `nix build git+https://git.eversole.co/James/tricu`
|
||||||
|
|
||||||
`./result/bin/tricu --help`
|
|
||||||
|
|
||||||
```
|
|
||||||
tricu Evaluator and REPL
|
|
||||||
|
|
||||||
tricu [COMMAND] ... [OPTIONS]
|
|
||||||
tricu: Exploring Tree Calculus
|
|
||||||
|
|
||||||
Common flags:
|
|
||||||
-? --help Display help message
|
|
||||||
-V --version Print version information
|
|
||||||
|
|
||||||
tricu [repl] [OPTIONS]
|
|
||||||
Start interactive REPL
|
|
||||||
|
|
||||||
tricu eval [OPTIONS]
|
|
||||||
Evaluate tricu and return the result of the final expression.
|
|
||||||
|
|
||||||
-f --file=FILE Input file path(s) for evaluation.
|
|
||||||
Defaults to stdin.
|
|
||||||
-t --form=FORM Optional output form: (tree|fsl|ast|ternary|ascii).
|
|
||||||
Defaults to tricu-compatible `t` tree form.
|
|
||||||
|
|
||||||
tricu decode [OPTIONS]
|
|
||||||
Decode a Tree Calculus value into a string representation.
|
|
||||||
|
|
||||||
-f --file=FILE Optional input file path to attempt decoding.
|
|
||||||
Defaults to stdin.
|
|
||||||
```
|
|
||||||
|
|
||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
|
|
||||||
Tree Calculus was discovered by [Barry Jay](https://github.com/barry-jay-personal/blog).
|
Tree Calculus was discovered by [Barry Jay](https://github.com/barry-jay-personal/blog).
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
-- 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 notation of empty nodes. Empty nodes can be represented
|
|
||||||
-- with an empty list, `[]`, which is equivalent to a single node `t`.
|
|
||||||
--
|
|
||||||
-- Example tree inputs:
|
|
||||||
-- [("1") [("2") [("4") t t] t] [("3") [("5") t t] [("6") t t]]]]
|
|
||||||
-- Graph:
|
|
||||||
-- 1
|
|
||||||
-- / \
|
|
||||||
-- 2 3
|
|
||||||
-- / / \
|
|
||||||
-- 4 5 6
|
|
||||||
--
|
|
||||||
|
|
||||||
isLeaf = (\node : lOr (emptyList node) (emptyList (tail node)))
|
|
||||||
getLabel = (\node : head node)
|
|
||||||
getLeft = (\node : if (emptyList node) [] (if (emptyList (tail node)) [] (head (tail node))))
|
|
||||||
getRight = (\node : if (emptyList node) [] (if (emptyList (tail node)) [] (if (emptyList (tail (tail node))) [] (head (tail (tail node))))))
|
|
||||||
|
|
||||||
processLevel = y (\self queue : if (emptyList queue) [] (pair (map getLabel queue) (self (filter (\node : not (emptyList node)) (lconcat (map getLeft queue) (map getRight queue))))))
|
|
||||||
levelOrderTraversal = (\a : processLevel (t a t))
|
|
||||||
toLineString = y (\self levels : if (emptyList levels) "" (lconcat (lconcat (map (\x : lconcat x " ") (head levels)) "") (if (emptyList (tail levels)) "" (lconcat (t (t 10 t) t) (self (tail levels))))))
|
|
||||||
levelOrderToString = (\s : toLineString (levelOrderTraversal s))
|
|
||||||
|
|
||||||
flatten = foldl (\acc x : lconcat acc x) ""
|
|
||||||
flatLOT = (\s : lconcat (t 10 t) (flatten (levelOrderToString s)))
|
|
||||||
|
|
||||||
exampleOne = flatLOT [("1") [("2") [("4") t t] t] [("3") [("5") t t] [("6") t t]]]]
|
|
||||||
exampleTwo = flatLOT [("1") [("2") [("4") [("8") t t] [("9") t t]] [("6") [("10") t t] [("12") t t]]] [("3") [("5") [("11") t t] t] [("7") t t]]]
|
|
41
lib/base.tri
41
lib/base.tri
@ -1,41 +0,0 @@
|
|||||||
false = t
|
|
||||||
_ = t
|
|
||||||
true = t t
|
|
||||||
k = t t
|
|
||||||
i = t (t k) t
|
|
||||||
s = t (t (k t)) t
|
|
||||||
m = s i i
|
|
||||||
b = s (k s) k
|
|
||||||
c = s (s (k s) (s (k k) s)) (k k)
|
|
||||||
iC = (\a b c : s a (k c) b)
|
|
||||||
iD = b (b iC) iC
|
|
||||||
iE = b (b iD) iC
|
|
||||||
yi = (\i : b m (c b (i m)))
|
|
||||||
y = yi iC
|
|
||||||
yC = yi iD
|
|
||||||
yD = yi iE
|
|
||||||
id = (\a : a)
|
|
||||||
triage = (\a b c : t (t a b) c)
|
|
||||||
pair = t
|
|
||||||
matchBool = (\ot of : triage of (\_ : ot) (\_ _ : ot))
|
|
||||||
matchList = (\oe oc : triage oe _ oc)
|
|
||||||
matchPair = (\op : triage _ _ op)
|
|
||||||
not = matchBool false true
|
|
||||||
and = matchBool id (\z : false)
|
|
||||||
if = (\cond then else : t (t else (t t then)) t cond)
|
|
||||||
test = triage "Leaf" (\z : "Stem") (\a b : "Fork")
|
|
||||||
emptyList = matchList true (\y z : false)
|
|
||||||
head = matchList t (\hd tl : hd)
|
|
||||||
tail = matchList t (\hd tl : tl)
|
|
||||||
lconcat = y (\self : matchList (\k : k) (\h r k : pair h (self r k)))
|
|
||||||
lAnd = triage (\x : false) (\_ x : x) (\_ _ x : x)
|
|
||||||
lOr = triage (\x : x) (\_ _ : true) (\_ _ x : true)
|
|
||||||
hmap = y (\self : matchList (\f : t) (\hd tl f : pair (f hd) (self tl f)))
|
|
||||||
map = (\f l : hmap l f)
|
|
||||||
equal = y (\self : triage (triage true (\z : false) (\y z : false)) (\ax : triage false (self ax) (\y z : false)) (\ax ay : triage false (\z : false) (\bx by : lAnd (self ax bx) (self ay by))))
|
|
||||||
hfilter = y (\self : matchList (\f : t) (\hd tl f : matchBool (t hd) i (f hd) (self tl f)))
|
|
||||||
filter = (\f l : hfilter l f)
|
|
||||||
hfoldl = y (\self f l x : matchList (\acc : acc) (\hd tl acc : self f tl (f acc hd)) l x)
|
|
||||||
foldl = (\f x l : hfoldl f l x)
|
|
||||||
hfoldr = y (\self x f l : matchList x (\hd tl : f (self x f tl) hd) l)
|
|
||||||
foldr = (\f x l : hfoldr x f l)
|
|
8
shell.nix
Normal file
8
shell.nix
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
let x = pkgs.haskellPackages.ghcWithPackages (hpkgs: with hpkgs; [
|
||||||
|
megaparsec
|
||||||
|
]);
|
||||||
|
in
|
||||||
|
pkgs.mkShell {
|
||||||
|
buildInputs = [ x ];
|
||||||
|
}
|
19
src/Compiler.hs
Normal file
19
src/Compiler.hs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
module Compiler where
|
||||||
|
|
||||||
|
import Eval
|
||||||
|
import Library
|
||||||
|
import Parser
|
||||||
|
import Research
|
||||||
|
|
||||||
|
import System.IO
|
||||||
|
|
||||||
|
import qualified Data.Map as Map
|
||||||
|
|
||||||
|
evaluateFile :: FilePath -> IO T
|
||||||
|
evaluateFile filePath = do
|
||||||
|
contents <- readFile filePath
|
||||||
|
let asts = parseTricu contents
|
||||||
|
let finalEnv = evalTricu library asts
|
||||||
|
case Map.lookup "__result" finalEnv of
|
||||||
|
Just finalResult -> return finalResult
|
||||||
|
Nothing -> error "No result found in final environment"
|
23
src/Eval.hs
23
src/Eval.hs
@ -3,7 +3,8 @@ module Eval where
|
|||||||
import Parser
|
import Parser
|
||||||
import Research
|
import Research
|
||||||
|
|
||||||
import Data.Map (Map)
|
import Data.Map (Map)
|
||||||
|
import Data.List (foldl')
|
||||||
|
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
@ -13,7 +14,7 @@ evalSingle env term = case term of
|
|||||||
SFunc name [] body ->
|
SFunc name [] body ->
|
||||||
let lineNoLambda = eliminateLambda body
|
let lineNoLambda = eliminateLambda body
|
||||||
result = evalAST env lineNoLambda
|
result = evalAST env lineNoLambda
|
||||||
in Map.insert "__result" result (Map.insert name result env)
|
in Map.insert name result env
|
||||||
SLambda _ body ->
|
SLambda _ body ->
|
||||||
let result = evalAST env body
|
let result = evalAST env body
|
||||||
in Map.insert "__result" result env
|
in Map.insert "__result" result env
|
||||||
@ -23,7 +24,7 @@ evalSingle env term = case term of
|
|||||||
SVar name ->
|
SVar name ->
|
||||||
case Map.lookup name env of
|
case Map.lookup name env of
|
||||||
Just value -> Map.insert "__result" value env
|
Just value -> Map.insert "__result" value env
|
||||||
Nothing -> errorWithoutStackTrace $ "Variable " ++ name ++ " not defined"
|
Nothing -> error $ "Variable " ++ name ++ " not defined"
|
||||||
_ ->
|
_ ->
|
||||||
let result = evalAST env term
|
let result = evalAST env term
|
||||||
in Map.insert "__result" result env
|
in Map.insert "__result" result env
|
||||||
@ -46,18 +47,19 @@ evalAST :: Map String T -> TricuAST -> T
|
|||||||
evalAST env term = case term of
|
evalAST env term = case term of
|
||||||
SVar name -> case Map.lookup name env of
|
SVar name -> case Map.lookup name env of
|
||||||
Just value -> value
|
Just value -> value
|
||||||
Nothing -> errorWithoutStackTrace $ "Variable " ++ name ++ " not defined"
|
Nothing -> error $ "Variable " ++ name ++ " not defined"
|
||||||
TLeaf -> Leaf
|
TLeaf -> Leaf
|
||||||
TStem t -> Stem (evalAST env t)
|
TStem t -> Stem (evalAST env t)
|
||||||
TFork t1 t2 -> Fork (evalAST env t1) (evalAST env t2)
|
TFork t1 t2 -> Fork (evalAST env t1) (evalAST env t2)
|
||||||
SApp t1 t2 -> apply (evalAST env t1) (evalAST env t2)
|
SApp t1 t2 -> apply (evalAST env t1) (evalAST env t2)
|
||||||
SStr str -> ofString str
|
SStr str -> ofString str
|
||||||
SInt num -> ofNumber num
|
SInt num -> ofNumber num
|
||||||
SList elems -> ofList (map (evalAST env) elems)
|
SList elems -> ofList (map (evalAST Map.empty) elems)
|
||||||
SEmpty -> Leaf
|
SEmpty -> Leaf
|
||||||
SFunc name args body ->
|
SFunc name args body ->
|
||||||
errorWithoutStackTrace $ "Unexpected function definition " ++ name
|
error $ "Unexpected function definition " ++ name
|
||||||
SLambda {} -> errorWithoutStackTrace "Internal error: SLambda found in evalAST after elimination."
|
++ " in evalAST; define via evalSingle."
|
||||||
|
SLambda {} -> error "Internal error: SLambda found in evalAST after elimination."
|
||||||
|
|
||||||
eliminateLambda :: TricuAST -> TricuAST
|
eliminateLambda :: TricuAST -> TricuAST
|
||||||
eliminateLambda (SLambda (v:vs) body)
|
eliminateLambda (SLambda (v:vs) body)
|
||||||
@ -100,6 +102,11 @@ freeVars (SLambda vs b) = foldr Set.delete (freeVars b) vs
|
|||||||
isFree :: String -> TricuAST -> Bool
|
isFree :: String -> TricuAST -> Bool
|
||||||
isFree x = Set.member x . freeVars
|
isFree x = Set.member x . freeVars
|
||||||
|
|
||||||
|
toAST :: T -> TricuAST
|
||||||
|
toAST Leaf = TLeaf
|
||||||
|
toAST (Stem a) = TStem (toAST a)
|
||||||
|
toAST (Fork a b) = TFork (toAST a) (toAST b)
|
||||||
|
|
||||||
-- We need the SKI operators in an unevaluated TricuAST tree form so that we
|
-- We need the SKI operators in an unevaluated TricuAST tree form so that we
|
||||||
-- can keep the evaluation functions straightforward
|
-- can keep the evaluation functions straightforward
|
||||||
tI :: TricuAST
|
tI :: TricuAST
|
||||||
@ -114,4 +121,4 @@ tS = SApp (SApp TLeaf (SApp TLeaf (SApp (SApp TLeaf TLeaf) TLeaf))) TLeaf
|
|||||||
result :: Map String T -> T
|
result :: Map String T -> 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 -> error "No __result field found in provided environment"
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
module FileEval where
|
|
||||||
|
|
||||||
import Eval
|
|
||||||
import Parser
|
|
||||||
import Research
|
|
||||||
|
|
||||||
import System.IO
|
|
||||||
|
|
||||||
import qualified Data.Map as Map
|
|
||||||
|
|
||||||
evaluateFileResult :: FilePath -> IO T
|
|
||||||
evaluateFileResult filePath = do
|
|
||||||
contents <- readFile filePath
|
|
||||||
let asts = parseTricu contents
|
|
||||||
let finalEnv = evalTricu Map.empty asts
|
|
||||||
case Map.lookup "__result" finalEnv of
|
|
||||||
Just finalResult -> return finalResult
|
|
||||||
Nothing -> errorWithoutStackTrace "No expressions to evaluate found"
|
|
||||||
|
|
||||||
evaluateFile :: FilePath -> IO Env
|
|
||||||
evaluateFile filePath = do
|
|
||||||
contents <- readFile filePath
|
|
||||||
let asts = parseTricu contents
|
|
||||||
pure $ evalTricu Map.empty asts
|
|
||||||
|
|
||||||
evaluateFileWithContext :: Env -> FilePath -> IO Env
|
|
||||||
evaluateFileWithContext env filePath = do
|
|
||||||
contents <- readFile filePath
|
|
||||||
let asts = parseTricu contents
|
|
||||||
pure $ evalTricu env asts
|
|
34
src/Lexer.hs
34
src/Lexer.hs
@ -1,25 +1,38 @@
|
|||||||
module Lexer where
|
module Lexer where
|
||||||
|
|
||||||
import Research
|
import Research
|
||||||
|
|
||||||
import Control.Monad (void)
|
|
||||||
import Data.Void
|
|
||||||
import Text.Megaparsec
|
import Text.Megaparsec
|
||||||
import Text.Megaparsec.Char hiding (space)
|
import Text.Megaparsec.Char hiding (space)
|
||||||
import Text.Megaparsec.Char.Lexer
|
import Text.Megaparsec.Char.Lexer
|
||||||
|
|
||||||
|
import Control.Monad (void)
|
||||||
|
import Data.Void
|
||||||
|
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
|
|
||||||
type Lexer = Parsec Void String
|
type Lexer = Parsec Void String
|
||||||
|
|
||||||
|
data LToken
|
||||||
|
= LKeywordT
|
||||||
|
| LIdentifier String
|
||||||
|
| LIntegerLiteral Int
|
||||||
|
| LStringLiteral String
|
||||||
|
| LAssign
|
||||||
|
| LColon
|
||||||
|
| LBackslash
|
||||||
|
| LOpenParen
|
||||||
|
| LCloseParen
|
||||||
|
| LOpenBracket
|
||||||
|
| LCloseBracket
|
||||||
|
| LNewline
|
||||||
|
deriving (Show, Eq, Ord)
|
||||||
|
|
||||||
keywordT :: Lexer LToken
|
keywordT :: Lexer LToken
|
||||||
keywordT = string "t" *> notFollowedBy alphaNumChar *> pure LKeywordT
|
keywordT = string "t" *> notFollowedBy alphaNumChar *> pure LKeywordT
|
||||||
|
|
||||||
identifier :: Lexer LToken
|
identifier :: Lexer LToken
|
||||||
identifier = do
|
identifier = do
|
||||||
first <- letterChar <|> char '_'
|
name <- some (letterChar <|> char '_' <|> char '-')
|
||||||
rest <- many (letterChar <|> char '_' <|> char '-' <|> digitChar)
|
|
||||||
let name = first : rest
|
|
||||||
if (name == "t" || name == "__result")
|
if (name == "t" || name == "__result")
|
||||||
then fail "Keywords (`t`, `__result`) cannot be used as an identifier"
|
then fail "Keywords (`t`, `__result`) cannot be used as an identifier"
|
||||||
else return (LIdentifier name)
|
else return (LIdentifier name)
|
||||||
@ -33,8 +46,11 @@ stringLiteral :: Lexer LToken
|
|||||||
stringLiteral = do
|
stringLiteral = do
|
||||||
char '"'
|
char '"'
|
||||||
content <- many (noneOf ['"'])
|
content <- many (noneOf ['"'])
|
||||||
char '"' --"
|
if null content
|
||||||
return (LStringLiteral content)
|
then fail "Empty string literals are not allowed"
|
||||||
|
else do
|
||||||
|
char '"' --"
|
||||||
|
return (LStringLiteral content)
|
||||||
|
|
||||||
assign :: Lexer LToken
|
assign :: Lexer LToken
|
||||||
assign = char '=' *> pure LAssign
|
assign = char '=' *> pure LAssign
|
||||||
@ -90,5 +106,5 @@ tricuLexer = do
|
|||||||
|
|
||||||
lexTricu :: String -> [LToken]
|
lexTricu :: String -> [LToken]
|
||||||
lexTricu input = case runParser tricuLexer "" input of
|
lexTricu input = case runParser tricuLexer "" input of
|
||||||
Left err -> errorWithoutStackTrace $ "Lexical error:\n" ++ errorBundlePretty err
|
Left err -> error $ "Lexical error:\n" ++ errorBundlePretty err
|
||||||
Right tokens -> tokens
|
Right tokens -> tokens
|
||||||
|
46
src/Library.hs
Normal file
46
src/Library.hs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
module Library where
|
||||||
|
|
||||||
|
import Eval
|
||||||
|
import Parser
|
||||||
|
import Research
|
||||||
|
|
||||||
|
import qualified Data.Map as Map
|
||||||
|
|
||||||
|
library :: Env
|
||||||
|
library = evalTricu Map.empty $ parseTricu $ unlines
|
||||||
|
[ "false = t"
|
||||||
|
, "true = t t"
|
||||||
|
, "_ = t"
|
||||||
|
, "k = t t"
|
||||||
|
, "i = t (t k) t"
|
||||||
|
, "s = t (t (k t)) t"
|
||||||
|
, "m = s i i"
|
||||||
|
, "b = s (k s) k"
|
||||||
|
, "c = s (s (k s) (s (k k) s)) (k k)"
|
||||||
|
, "iC = (\\a b c : s a (k c) b)"
|
||||||
|
, "iD = b (b iC) iC"
|
||||||
|
, "iE = b (b iD) iC"
|
||||||
|
, "yi = (\\i : b m (c b (i m)))"
|
||||||
|
, "y = yi iC"
|
||||||
|
, "yC = yi iD"
|
||||||
|
, "yD = yi iE"
|
||||||
|
, "id = (\\a : a)"
|
||||||
|
, "triage = (\\a b c : t (t a b) c)"
|
||||||
|
, "pair = t"
|
||||||
|
, "matchBool = (\\ot of : triage of (\\_ : ot) (\\_ _ : ot))"
|
||||||
|
, "matchList = (\\oe oc : triage oe _ oc)"
|
||||||
|
, "matchPair = (\\op : triage _ _ op)"
|
||||||
|
, "not = matchBool false true"
|
||||||
|
, "and = matchBool id (\\z : false)"
|
||||||
|
, "if = (\\cond then else : t (t else (t t then)) t cond)"
|
||||||
|
, "test = triage \"Leaf\" (\\z : \"Stem\") (\\a b : \"Fork\")"
|
||||||
|
, "emptyList = matchList true (\\y z : false)"
|
||||||
|
, "head = matchList t (\\hd tl : hd)"
|
||||||
|
, "tail = matchList t (\\hd tl : tl)"
|
||||||
|
, "lconcat = y (\\self : matchList (\\k : k) (\\h r k : pair h (self r k)))"
|
||||||
|
, "lAnd = triage (\\x : false) (\\_ x : x) (\\_ _ x : x)"
|
||||||
|
, "lOr = triage (\\x : x) (\\_ _ : true) (\\_ _ x : true)"
|
||||||
|
, "hmap = y (\\self : matchList (\\f : t) (\\hd tl f : pair (f hd) (self tl f)))"
|
||||||
|
, "map = (\\f l : hmap l f)"
|
||||||
|
, "equal = y (\\self : triage (triage true (\\z : false) (\\y z : false)) (\\ax : triage false (self ax) (\\y z : false)) (\\ax ay : triage false (\\z : false) (\\bx by : lAnd (self ax bx) (self ay by))))"
|
||||||
|
]
|
87
src/Main.hs
87
src/Main.hs
@ -1,22 +1,24 @@
|
|||||||
module Main where
|
module Main where
|
||||||
|
|
||||||
import Eval (evalTricu, result)
|
import Compiler
|
||||||
import FileEval
|
import Eval (evalTricu, result, toAST)
|
||||||
import Parser (parseTricu)
|
import Library (library)
|
||||||
|
import Parser (parseTricu)
|
||||||
import REPL
|
import REPL
|
||||||
import Research
|
import Research
|
||||||
|
|
||||||
import Control.Monad (foldM)
|
import Text.Megaparsec (runParser)
|
||||||
import Control.Monad.IO.Class (liftIO)
|
|
||||||
import Text.Megaparsec (runParser)
|
|
||||||
import System.Console.CmdArgs
|
import System.Console.CmdArgs
|
||||||
|
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
|
|
||||||
data TricuArgs
|
data TricuArgs
|
||||||
= Repl
|
= Repl
|
||||||
| Evaluate { file :: [FilePath], form :: EvaluatedForm }
|
| Compile { file :: FilePath, output :: Maybe FilePath, form :: CompiledForm }
|
||||||
| Decode { file :: [FilePath] }
|
| Decode { input :: Maybe FilePath }
|
||||||
|
deriving (Show, Data, Typeable)
|
||||||
|
|
||||||
|
data CompiledForm = TreeCalculus | AST | Ternary | Ascii
|
||||||
deriving (Show, Data, Typeable)
|
deriving (Show, Data, Typeable)
|
||||||
|
|
||||||
replMode :: TricuArgs
|
replMode :: TricuArgs
|
||||||
@ -25,60 +27,57 @@ replMode = Repl
|
|||||||
&= auto
|
&= auto
|
||||||
&= name "repl"
|
&= name "repl"
|
||||||
|
|
||||||
evaluateMode :: TricuArgs
|
compileMode :: TricuArgs
|
||||||
evaluateMode = Evaluate
|
compileMode = Compile
|
||||||
{ file = def &= help "Input file path(s) for evaluation.\n \
|
{ file = def &= typ "FILE"
|
||||||
\ Defaults to stdin."
|
&= help "Relative or absolute path to file input for compilation" &= name "f"
|
||||||
&= name "f" &= typ "FILE"
|
, output = def &= typ "OUTPUT"
|
||||||
, form = TreeCalculus &= typ "FORM"
|
&= help "Optional output file path for resulting output" &= name "o"
|
||||||
&= help "Optional output form: (tree|fsl|ast|ternary|ascii).\n \
|
, form = TreeCalculus &= typ "FORM"
|
||||||
\ Defaults to tricu-compatible `t` tree form."
|
&= help "Output form: (tree|ast|ternary|ascii)"
|
||||||
&= name "t"
|
&= name "t"
|
||||||
}
|
}
|
||||||
&= help "Evaluate tricu and return the result of the final expression."
|
&= help "Compile a file and return the result of the expression in the final line"
|
||||||
&= explicit
|
&= explicit
|
||||||
&= name "eval"
|
&= name "compile"
|
||||||
|
|
||||||
decodeMode :: TricuArgs
|
decodeMode :: TricuArgs
|
||||||
decodeMode = Decode
|
decodeMode = Decode
|
||||||
{ file = def
|
{ input = def &= typ "FILE"
|
||||||
&= help "Optional input file path to attempt decoding.\n \
|
&= help "Optional file path containing a Tree Calculus value. Defaults to stdin." &= name "f"
|
||||||
\ Defaults to stdin."
|
|
||||||
&= name "f" &= typ "FILE"
|
|
||||||
}
|
}
|
||||||
&= help "Decode a Tree Calculus value into a string representation."
|
&= help "Decode a Tree Calculus value into a string representation"
|
||||||
&= explicit
|
&= explicit
|
||||||
&= name "decode"
|
&= name "decode"
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
args <- cmdArgs $ modes [replMode, evaluateMode, decodeMode]
|
args <- cmdArgs $ modes [replMode, compileMode, decodeMode]
|
||||||
&= help "tricu: Exploring Tree Calculus"
|
&= help "tricu: Exploring Tree Calculus"
|
||||||
&= program "tricu"
|
&= program "tricu"
|
||||||
&= summary "tricu Evaluator and REPL"
|
&= summary "tricu - compiler and repl"
|
||||||
|
|
||||||
case args of
|
case args of
|
||||||
Repl -> do
|
Repl -> do
|
||||||
putStrLn "Welcome to the tricu REPL"
|
putStrLn "Welcome to the tricu REPL"
|
||||||
putStrLn "You can exit with `CTRL+D` or the `:_exit` command.`"
|
putStrLn "You can exit with `CTRL+D` or the `:_exit` command.`"
|
||||||
library <- liftIO $ evaluateFile "./lib/base.tri"
|
repl library
|
||||||
repl $ Map.delete "__result" library
|
Compile { file = filePath, output = maybeOutputPath, form = form } -> do
|
||||||
Evaluate { file = filePaths, form = form } -> do
|
result <- evaluateFile filePath
|
||||||
result <- case filePaths of
|
|
||||||
[] -> do
|
|
||||||
t <- getContents
|
|
||||||
pure $ runTricu t
|
|
||||||
(filePath:restFilePaths) -> do
|
|
||||||
initialEnv <- evaluateFile filePath
|
|
||||||
finalEnv <- foldM evaluateFileWithContext initialEnv restFilePaths
|
|
||||||
pure $ result finalEnv
|
|
||||||
let fRes = formatResult form result
|
let fRes = formatResult form result
|
||||||
putStr fRes
|
case maybeOutputPath of
|
||||||
Decode { file = filePaths } -> do
|
Just outputPath -> do
|
||||||
value <- case filePaths of
|
writeFile outputPath fRes
|
||||||
[] -> getContents
|
putStrLn $ "Output to: " ++ outputPath
|
||||||
(filePath:_) -> readFile filePath
|
Nothing -> putStr fRes
|
||||||
library <- liftIO $ evaluateFile "./lib/base.tri"
|
Decode { input = maybeInputPath } -> do
|
||||||
|
value <- case maybeInputPath of
|
||||||
|
Just inputPath -> readFile inputPath
|
||||||
|
Nothing -> getContents
|
||||||
putStrLn $ decodeResult $ result $ evalTricu library $ parseTricu value
|
putStrLn $ decodeResult $ result $ evalTricu library $ parseTricu value
|
||||||
|
|
||||||
runTricu :: String -> T
|
formatResult :: CompiledForm -> T -> String
|
||||||
runTricu = result . evalTricu Map.empty . parseTricu
|
formatResult TreeCalculus = show
|
||||||
|
formatResult AST = show . toAST
|
||||||
|
formatResult Ternary = toTernaryString
|
||||||
|
formatResult Ascii = toAscii
|
||||||
|
@ -1,19 +1,33 @@
|
|||||||
module Parser where
|
module Parser where
|
||||||
|
|
||||||
import Lexer
|
import Lexer
|
||||||
import Research hiding (toList)
|
import Research hiding (toList)
|
||||||
|
|
||||||
import Data.List.NonEmpty (toList)
|
import Data.List.NonEmpty (toList)
|
||||||
import Data.Void (Void)
|
import Data.Void (Void)
|
||||||
import Text.Megaparsec
|
import Text.Megaparsec
|
||||||
import Text.Megaparsec.Char
|
import Text.Megaparsec.Char
|
||||||
import Text.Megaparsec.Error (ParseErrorBundle, errorBundlePretty)
|
import Text.Megaparsec.Error (errorBundlePretty, ParseErrorBundle)
|
||||||
|
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
|
|
||||||
type Parser = Parsec Void [LToken]
|
type Parser = Parsec Void [LToken]
|
||||||
type AltParser = Parsec Void String
|
type AltParser = Parsec Void String
|
||||||
|
|
||||||
|
data TricuAST
|
||||||
|
= SVar String
|
||||||
|
| SInt Int
|
||||||
|
| SStr String
|
||||||
|
| SList [TricuAST]
|
||||||
|
| SFunc String [String] TricuAST
|
||||||
|
| SApp TricuAST TricuAST
|
||||||
|
| TLeaf
|
||||||
|
| TStem TricuAST
|
||||||
|
| TFork TricuAST TricuAST
|
||||||
|
| SLambda [String] TricuAST
|
||||||
|
| SEmpty
|
||||||
|
deriving (Show, Eq, Ord)
|
||||||
|
|
||||||
parseTricu :: String -> [TricuAST]
|
parseTricu :: String -> [TricuAST]
|
||||||
parseTricu input
|
parseTricu input
|
||||||
| null tokens = []
|
| null tokens = []
|
||||||
@ -54,9 +68,16 @@ parseFunction = do
|
|||||||
|
|
||||||
parseAtomicBase :: Parser TricuAST
|
parseAtomicBase :: Parser TricuAST
|
||||||
parseAtomicBase = choice
|
parseAtomicBase = choice
|
||||||
[ parseTreeLeaf
|
[ try parseVarWithoutAssignment
|
||||||
|
, parseTreeLeaf
|
||||||
, parseGrouped
|
, parseGrouped
|
||||||
]
|
]
|
||||||
|
parseVarWithoutAssignment :: Parser TricuAST
|
||||||
|
parseVarWithoutAssignment = do
|
||||||
|
LIdentifier name <- satisfy isIdentifier
|
||||||
|
if (name == "t" || name == "__result")
|
||||||
|
then fail $ "Reserved keyword: " ++ name ++ " cannot be assigned."
|
||||||
|
else notFollowedBy (satisfy (== LAssign)) *> return (SVar name)
|
||||||
|
|
||||||
parseLambda :: Parser TricuAST
|
parseLambda :: Parser TricuAST
|
||||||
parseLambda = between (satisfy (== LOpenParen)) (satisfy (== LCloseParen)) $ do
|
parseLambda = between (satisfy (== LOpenParen)) (satisfy (== LCloseParen)) $ do
|
||||||
|
43
src/REPL.hs
43
src/REPL.hs
@ -1,15 +1,13 @@
|
|||||||
module REPL where
|
module REPL where
|
||||||
|
|
||||||
import Eval
|
import Eval
|
||||||
import FileEval
|
|
||||||
import Lexer
|
import Lexer
|
||||||
import Parser
|
import Parser
|
||||||
import Research
|
import Research
|
||||||
|
|
||||||
import Control.Exception (SomeException, catch)
|
import Control.Exception (SomeException, catch)
|
||||||
import Control.Monad.IO.Class (liftIO)
|
import Control.Monad.IO.Class (liftIO)
|
||||||
import Data.Char (isSpace)
|
import Data.List (intercalate)
|
||||||
import Data.List (dropWhile, dropWhileEnd, intercalate)
|
|
||||||
import System.Console.Haskeline
|
import System.Console.Haskeline
|
||||||
|
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
@ -21,27 +19,14 @@ repl env = runInputT defaultSettings (loop env)
|
|||||||
loop env = do
|
loop env = do
|
||||||
minput <- getInputLine "tricu < "
|
minput <- getInputLine "tricu < "
|
||||||
case minput of
|
case minput of
|
||||||
Nothing -> outputStrLn "Exiting tricu"
|
Nothing -> outputStrLn "Goodbye!"
|
||||||
Just s -> case strip s of
|
Just ":_exit" -> outputStrLn "Goodbye!"
|
||||||
"!exit" -> outputStrLn "Exiting tricu"
|
Just "" -> do
|
||||||
"!load" -> do
|
outputStrLn ""
|
||||||
path <- getInputLine "File path to load < "
|
loop env
|
||||||
case path of
|
Just input -> do
|
||||||
Nothing -> do
|
newEnv <- liftIO $ (processInput env input `catch` errorHandler env)
|
||||||
outputStrLn "No input received; stopping import."
|
loop newEnv
|
||||||
loop env
|
|
||||||
Just path -> do
|
|
||||||
loadedEnv <- liftIO $ evaluateFileWithContext env (strip path)
|
|
||||||
loop $ Map.delete "__result" (Map.union loadedEnv env)
|
|
||||||
"" -> do
|
|
||||||
outputStrLn ""
|
|
||||||
loop env
|
|
||||||
input -> do
|
|
||||||
case (take 2 input) of
|
|
||||||
"--" -> loop env
|
|
||||||
_ -> do
|
|
||||||
newEnv <- liftIO $ (processInput env input `catch` errorHandler env)
|
|
||||||
loop newEnv
|
|
||||||
|
|
||||||
processInput :: Env -> String -> IO Env
|
processInput :: Env -> String -> IO Env
|
||||||
processInput env input = do
|
processInput env input = do
|
||||||
@ -49,7 +34,8 @@ repl env = runInputT defaultSettings (loop env)
|
|||||||
newEnv = evalTricu env asts
|
newEnv = evalTricu env asts
|
||||||
case Map.lookup "__result" newEnv of
|
case Map.lookup "__result" newEnv of
|
||||||
Just r -> do
|
Just r -> do
|
||||||
putStrLn $ "tricu > " ++ decodeResult r
|
putStrLn $ "tricu > " ++ show r
|
||||||
|
putStrLn $ "READ -: \"" ++ decodeResult r ++ "\""
|
||||||
Nothing -> return ()
|
Nothing -> return ()
|
||||||
return newEnv
|
return newEnv
|
||||||
|
|
||||||
@ -57,15 +43,12 @@ repl env = runInputT defaultSettings (loop env)
|
|||||||
errorHandler env e = do
|
errorHandler env e = do
|
||||||
putStrLn $ "Error: " ++ show e
|
putStrLn $ "Error: " ++ show e
|
||||||
return env
|
return env
|
||||||
|
|
||||||
strip :: String -> String
|
|
||||||
strip = dropWhileEnd isSpace . dropWhile isSpace
|
|
||||||
|
|
||||||
decodeResult :: T -> String
|
decodeResult :: T -> String
|
||||||
decodeResult tc = case toNumber tc of
|
decodeResult tc = case toNumber tc of
|
||||||
Right num -> show num
|
Right num -> show num
|
||||||
Left _ -> case toString tc of
|
Left _ -> case toString tc of
|
||||||
Right str -> "\"" ++ str ++ "\""
|
Right str -> str
|
||||||
Left _ -> case toList tc of
|
Left _ -> case toList tc of
|
||||||
Right list -> "[" ++ intercalate ", " (map decodeResult list) ++ "]"
|
Right list -> "[" ++ intercalate ", " (map decodeResult list) ++ "]"
|
||||||
Left _ -> formatResult TreeCalculus tc
|
Left _ -> ""
|
||||||
|
108
src/Research.hs
108
src/Research.hs
@ -1,57 +1,14 @@
|
|||||||
module Research where
|
module Research where
|
||||||
|
|
||||||
import Control.Monad.State
|
import Control.Monad.State
|
||||||
import Data.List (intercalate)
|
import Data.List (intercalate)
|
||||||
import Data.Map (Map)
|
import Data.Map (Map)
|
||||||
import Data.Text (Text, replace)
|
|
||||||
import System.Console.CmdArgs (Data, Typeable)
|
|
||||||
|
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
import qualified Data.Text as T
|
|
||||||
|
|
||||||
-- Tree Calculus Types
|
|
||||||
data T = Leaf | Stem T | Fork T T
|
data T = Leaf | Stem T | Fork T T
|
||||||
deriving (Show, Eq, Ord)
|
deriving (Show, Eq, Ord)
|
||||||
|
|
||||||
-- Abstract Syntax Tree for tricu
|
|
||||||
data TricuAST
|
|
||||||
= SVar String
|
|
||||||
| SInt Int
|
|
||||||
| SStr String
|
|
||||||
| SList [TricuAST]
|
|
||||||
| SFunc String [String] TricuAST
|
|
||||||
| SApp TricuAST TricuAST
|
|
||||||
| TLeaf
|
|
||||||
| TStem TricuAST
|
|
||||||
| TFork TricuAST TricuAST
|
|
||||||
| SLambda [String] TricuAST
|
|
||||||
| SEmpty
|
|
||||||
deriving (Show, Eq, Ord)
|
|
||||||
|
|
||||||
-- Tokens from Lexer
|
|
||||||
data LToken
|
|
||||||
= LKeywordT
|
|
||||||
| LIdentifier String
|
|
||||||
| LIntegerLiteral Int
|
|
||||||
| LStringLiteral String
|
|
||||||
| LAssign
|
|
||||||
| LColon
|
|
||||||
| LBackslash
|
|
||||||
| LOpenParen
|
|
||||||
| LCloseParen
|
|
||||||
| LOpenBracket
|
|
||||||
| LCloseBracket
|
|
||||||
| LNewline
|
|
||||||
deriving (Show, Eq, Ord)
|
|
||||||
|
|
||||||
-- Output formats
|
|
||||||
data EvaluatedForm = TreeCalculus | FSL | AST | Ternary | Ascii
|
|
||||||
deriving (Show, Data, Typeable)
|
|
||||||
|
|
||||||
-- Environment containing previously evaluated TC terms
|
|
||||||
type Env = Map.Map String T
|
|
||||||
|
|
||||||
-- Tree Calculus Reduction
|
|
||||||
apply :: T -> T -> T
|
apply :: T -> T -> T
|
||||||
apply Leaf b = Stem b
|
apply Leaf b = Stem b
|
||||||
apply (Stem a) b = Fork a b
|
apply (Stem a) b = Fork a b
|
||||||
@ -122,29 +79,13 @@ toList (Fork x rest) = case toList rest of
|
|||||||
toList _ = Left "Invalid Tree Calculus list"
|
toList _ = Left "Invalid Tree Calculus list"
|
||||||
|
|
||||||
-- Outputs
|
-- Outputs
|
||||||
formatResult :: EvaluatedForm -> T -> String
|
|
||||||
formatResult TreeCalculus = toSimpleT . show
|
|
||||||
formatResult FSL = show
|
|
||||||
formatResult AST = show . toAST
|
|
||||||
formatResult Ternary = toTernaryString
|
|
||||||
formatResult Ascii = toAscii
|
|
||||||
|
|
||||||
toSimpleT :: String -> String
|
|
||||||
toSimpleT s = T.unpack
|
|
||||||
$ replace "Fork" "t"
|
|
||||||
$ replace "Stem" "t"
|
|
||||||
$ replace "Leaf" "t"
|
|
||||||
$ (T.pack s)
|
|
||||||
|
|
||||||
toTernaryString :: T -> String
|
toTernaryString :: T -> String
|
||||||
toTernaryString Leaf = "0"
|
toTernaryString Leaf = "0"
|
||||||
toTernaryString (Stem t) = "1" ++ toTernaryString t
|
toTernaryString (Stem t) = "1" ++ toTernaryString t
|
||||||
toTernaryString (Fork t1 t2) = "2" ++ toTernaryString t1 ++ toTernaryString t2
|
toTernaryString (Fork t1 t2) = "2" ++ toTernaryString t1 ++ toTernaryString t2
|
||||||
|
|
||||||
toAST :: T -> TricuAST
|
-- Utility
|
||||||
toAST Leaf = TLeaf
|
type Env = Map.Map String T
|
||||||
toAST (Stem a) = TStem (toAST a)
|
|
||||||
toAST (Fork a b) = TFork (toAST a) (toAST b)
|
|
||||||
|
|
||||||
toAscii :: T -> String
|
toAscii :: T -> String
|
||||||
toAscii tree = go tree "" True
|
toAscii tree = go tree "" True
|
||||||
@ -160,4 +101,41 @@ toAscii tree = go tree "" True
|
|||||||
++ go left (prefix ++ (if isLast then " " else "| ")) False
|
++ go left (prefix ++ (if isLast then " " else "| ")) False
|
||||||
++ go right (prefix ++ (if isLast then " " else "| ")) True
|
++ go right (prefix ++ (if isLast then " " else "| ")) True
|
||||||
|
|
||||||
-- Utility
|
rules :: IO ()
|
||||||
|
rules = putStr $ header
|
||||||
|
++ (unlines $ tcRules)
|
||||||
|
++ (unlines $ haskellRules)
|
||||||
|
++ footer
|
||||||
|
where
|
||||||
|
tcRules :: [String]
|
||||||
|
tcRules =
|
||||||
|
[ "| |"
|
||||||
|
, "| ┌--------- | Tree Calculus | ---------┐ |"
|
||||||
|
, "| | 1. t t a b -> a | |"
|
||||||
|
, "| | 2. t (t a) b c -> a c (b c)| |"
|
||||||
|
, "| | 3a. t (t a b) c t -> a | |"
|
||||||
|
, "| | 3b. t (t a b) c (t u) -> b u | |"
|
||||||
|
, "| | 3c. t (t a b) c (t u v) -> c u v | |"
|
||||||
|
, "| └-------------------------------------┘ |"
|
||||||
|
, "| |"
|
||||||
|
]
|
||||||
|
haskellRules :: [String]
|
||||||
|
haskellRules =
|
||||||
|
[ "| ┌------------------------------ | Haskell | --------------------------------┐ |"
|
||||||
|
, "| | | |"
|
||||||
|
, "| | data T = Leaf | Stem T | Fork TT | |"
|
||||||
|
, "| | | |"
|
||||||
|
, "| | apply :: T -> T -> T | |"
|
||||||
|
, "| | apply Leaf b = Stem b | |"
|
||||||
|
, "| | apply (Stem a) b = Fork a b | |"
|
||||||
|
, "| | apply (Fork Leaf a) _ = a | |"
|
||||||
|
, "| | apply (Fork (Stem a1) a2) b = apply (apply a1 b) (apply a2 b) | |"
|
||||||
|
, "| | apply (Fork (Fork a1 a2) a3) Leaf = a1 | |"
|
||||||
|
, "| | apply (Fork (Fork a1 a2) a3) (Stem u) = apply a2 u | |"
|
||||||
|
, "| | apply (Fork (Fork a1 a2) a3) (Fork u v) = apply (apply a3 u) v | |"
|
||||||
|
, "| └---------------------------------------------------------------------------┘ |"
|
||||||
|
]
|
||||||
|
header :: String
|
||||||
|
header = "┌-------------------- | Rules for evaluating Tree Calculus | -------------------┐\n"
|
||||||
|
footer :: String
|
||||||
|
footer = "└-------------------- | Rules for evaluating Tree Calculus | -------------------┘\n"
|
||||||
|
51
test/Spec.hs
51
test/Spec.hs
@ -1,8 +1,9 @@
|
|||||||
module Main where
|
module Main where
|
||||||
|
|
||||||
|
import Compiler
|
||||||
import Eval
|
import Eval
|
||||||
import FileEval
|
|
||||||
import Lexer
|
import Lexer
|
||||||
|
import Library
|
||||||
import Parser
|
import Parser
|
||||||
import REPL
|
import REPL
|
||||||
import Research
|
import Research
|
||||||
@ -30,7 +31,7 @@ tests = testGroup "Tricu Tests"
|
|||||||
, evaluationTests
|
, evaluationTests
|
||||||
, lambdaEvalTests
|
, lambdaEvalTests
|
||||||
, libraryTests
|
, libraryTests
|
||||||
, fileEvaluationTests
|
, compilerTests
|
||||||
, propertyTests
|
, propertyTests
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -53,7 +54,7 @@ lexerTests = testGroup "Lexer Tests"
|
|||||||
expect = Right [LKeywordT, LStringLiteral "string", LIntegerLiteral 42]
|
expect = Right [LKeywordT, LStringLiteral "string", LIntegerLiteral 42]
|
||||||
runParser tricuLexer "" input @?= expect
|
runParser tricuLexer "" input @?= expect
|
||||||
, testCase "Lex invalid token" $ do
|
, testCase "Lex invalid token" $ do
|
||||||
let input = "&invalid"
|
let input = "$invalid"
|
||||||
case runParser tricuLexer "" input of
|
case runParser tricuLexer "" input of
|
||||||
Left _ -> return ()
|
Left _ -> return ()
|
||||||
Right _ -> assertFailure "Expected lexer to fail on invalid token"
|
Right _ -> assertFailure "Expected lexer to fail on invalid token"
|
||||||
@ -287,132 +288,106 @@ lambdaEvalTests = testGroup "Lambda Evaluation Tests"
|
|||||||
libraryTests :: TestTree
|
libraryTests :: TestTree
|
||||||
libraryTests = testGroup "Library Tests"
|
libraryTests = testGroup "Library Tests"
|
||||||
[ testCase "K combinator 1" $ do
|
[ testCase "K combinator 1" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "k (t) (t t)"
|
let input = "k (t) (t t)"
|
||||||
env = evalTricu library (parseTricu input)
|
env = evalTricu library (parseTricu input)
|
||||||
result env @?= Leaf
|
result env @?= Leaf
|
||||||
, testCase "K combinator 2" $ do
|
, testCase "K combinator 2" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "k (t t) (t)"
|
let input = "k (t t) (t)"
|
||||||
env = evalTricu library (parseTricu input)
|
env = evalTricu library (parseTricu input)
|
||||||
result env @?= Stem Leaf
|
result env @?= Stem Leaf
|
||||||
, testCase "K combinator 3" $ do
|
, testCase "K combinator 3" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "k (t t t) (t)"
|
let input = "k (t t t) (t)"
|
||||||
env = evalTricu library (parseTricu input)
|
env = evalTricu library (parseTricu input)
|
||||||
result env @?= Fork Leaf Leaf
|
result env @?= Fork Leaf Leaf
|
||||||
, testCase "S combinator" $ do
|
, testCase "S combinator" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "s (t) (t) (t)"
|
let input = "s (t) (t) (t)"
|
||||||
env = evalTricu library (parseTricu input)
|
env = evalTricu library (parseTricu input)
|
||||||
result env @?= Fork Leaf (Stem Leaf)
|
result env @?= Fork Leaf (Stem Leaf)
|
||||||
, testCase "SKK == I (fully expanded)" $ do
|
, testCase "SKK == I (fully expanded)" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "s k k"
|
let input = "s k k"
|
||||||
env = evalTricu library (parseTricu input)
|
env = evalTricu library (parseTricu input)
|
||||||
result env @?= Fork (Stem (Stem Leaf)) (Stem Leaf)
|
result env @?= Fork (Stem (Stem Leaf)) (Stem Leaf)
|
||||||
, testCase "I combinator" $ do
|
, testCase "I combinator" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "i not"
|
let input = "i not"
|
||||||
env = evalTricu library (parseTricu input)
|
env = evalTricu library (parseTricu input)
|
||||||
result env @?= Fork (Fork (Stem Leaf) (Fork Leaf Leaf)) (Fork Leaf (Fork Leaf Leaf))
|
result env @?= Fork (Fork (Stem Leaf) (Fork Leaf Leaf)) (Fork Leaf (Fork Leaf Leaf))
|
||||||
, testCase "Triage test Leaf" $ do
|
, testCase "Triage test Leaf" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "test t"
|
let input = "test t"
|
||||||
env = decodeResult $ result $ evalTricu library (parseTricu input)
|
env = decodeResult $ result $ evalTricu library (parseTricu input)
|
||||||
env @?= "\"Leaf\""
|
env @?= "Leaf"
|
||||||
, testCase "Triage test (Stem Leaf)" $ do
|
, testCase "Triage test (Stem Leaf)" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "test (t t)"
|
let input = "test (t t)"
|
||||||
env = decodeResult $ result $ evalTricu library (parseTricu input)
|
env = decodeResult $ result $ evalTricu library (parseTricu input)
|
||||||
env @?= "\"Stem\""
|
env @?= "Stem"
|
||||||
, testCase "Triage test (Fork Leaf Leaf)" $ do
|
, testCase "Triage test (Fork Leaf Leaf)" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "test (t t t)"
|
let input = "test (t t t)"
|
||||||
env = decodeResult $ result $ evalTricu library (parseTricu input)
|
env = decodeResult $ result $ evalTricu library (parseTricu input)
|
||||||
env @?= "\"Fork\""
|
env @?= "Fork"
|
||||||
, testCase "Boolean NOT: true" $ do
|
, testCase "Boolean NOT: true" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "not true"
|
let input = "not true"
|
||||||
env = result $ evalTricu library (parseTricu input)
|
env = result $ evalTricu library (parseTricu input)
|
||||||
env @?= Leaf
|
env @?= Leaf
|
||||||
, testCase "Boolean NOT: false" $ do
|
, testCase "Boolean NOT: false" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "not false"
|
let input = "not false"
|
||||||
env = result $ evalTricu library (parseTricu input)
|
env = result $ evalTricu library (parseTricu input)
|
||||||
env @?= Stem Leaf
|
env @?= Stem Leaf
|
||||||
, testCase "Boolean AND TF" $ do
|
, testCase "Boolean AND TF" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "and (t t) (t)"
|
let input = "and (t t) (t)"
|
||||||
env = evalTricu library (parseTricu input)
|
env = evalTricu library (parseTricu input)
|
||||||
result env @?= Leaf
|
result env @?= Leaf
|
||||||
, testCase "Boolean AND FT" $ do
|
, testCase "Boolean AND FT" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "and (t) (t t)"
|
let input = "and (t) (t t)"
|
||||||
env = evalTricu library (parseTricu input)
|
env = evalTricu library (parseTricu input)
|
||||||
result env @?= Leaf
|
result env @?= Leaf
|
||||||
, testCase "Boolean AND FF" $ do
|
, testCase "Boolean AND FF" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "and (t) (t)"
|
let input = "and (t) (t)"
|
||||||
env = evalTricu library (parseTricu input)
|
env = evalTricu library (parseTricu input)
|
||||||
result env @?= Leaf
|
result env @?= Leaf
|
||||||
, testCase "Boolean AND TT" $ do
|
, testCase "Boolean AND TT" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "and (t t) (t t)"
|
let input = "and (t t) (t t)"
|
||||||
env = evalTricu library (parseTricu input)
|
env = evalTricu library (parseTricu input)
|
||||||
result env @?= Stem Leaf
|
result env @?= Stem Leaf
|
||||||
, testCase "List head" $ do
|
, testCase "List head" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "head [(t) (t t) (t t t)]"
|
let input = "head [(t) (t t) (t t t)]"
|
||||||
env = evalTricu library (parseTricu input)
|
env = evalTricu library (parseTricu input)
|
||||||
result env @?= Leaf
|
result env @?= Leaf
|
||||||
, testCase "List tail" $ do
|
, testCase "List tail" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "head (tail (tail [(t) (t t) (t t t)]))"
|
let input = "head (tail (tail [(t) (t t) (t t t)]))"
|
||||||
env = evalTricu library (parseTricu input)
|
env = evalTricu library (parseTricu input)
|
||||||
result env @?= Fork Leaf Leaf
|
result env @?= Fork Leaf Leaf
|
||||||
, testCase "List map" $ do
|
, testCase "List map" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "head (tail (map (\\a : (t t t)) [(t) (t) (t)]))"
|
let input = "head (tail (map (\\a : (t t t)) [(t) (t) (t)]))"
|
||||||
env = evalTricu library (parseTricu input)
|
env = evalTricu library (parseTricu input)
|
||||||
result env @?= Fork Leaf Leaf
|
result env @?= Fork Leaf Leaf
|
||||||
, testCase "Empty list check" $ do
|
, testCase "Empty list check" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "emptyList []"
|
let input = "emptyList []"
|
||||||
env = evalTricu library (parseTricu input)
|
env = evalTricu library (parseTricu input)
|
||||||
result env @?= Stem Leaf
|
result env @?= Stem Leaf
|
||||||
, testCase "Non-empty list check" $ do
|
, testCase "Non-empty list check" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "not (emptyList [(1) (2) (3)])"
|
let input = "not (emptyList [(1) (2) (3)])"
|
||||||
env = evalTricu library (parseTricu input)
|
env = evalTricu library (parseTricu input)
|
||||||
result env @?= Stem Leaf
|
result env @?= Stem Leaf
|
||||||
, testCase "Concatenate strings" $ do
|
, testCase "Concatenate strings" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "lconcat \"Hello, \" \"world!\""
|
let input = "lconcat \"Hello, \" \"world!\""
|
||||||
env = decodeResult $ result $ evalTricu library (parseTricu input)
|
env = decodeResult $ result $ evalTricu library (parseTricu input)
|
||||||
env @?= "\"Hello, world!\""
|
env @?= "Hello, world!"
|
||||||
, testCase "Verifying Equality" $ do
|
, testCase "Verifying Equality" $ do
|
||||||
library <- evaluateFile "./lib/base.tri"
|
|
||||||
let input = "equal (t t t) (t t t)"
|
let input = "equal (t t t) (t t t)"
|
||||||
env = evalTricu library (parseTricu input)
|
env = evalTricu library (parseTricu input)
|
||||||
result env @?= Stem Leaf
|
result env @?= Stem Leaf
|
||||||
]
|
]
|
||||||
|
|
||||||
fileEvaluationTests :: TestTree
|
compilerTests :: TestTree
|
||||||
fileEvaluationTests = testGroup "Evaluation tests"
|
compilerTests = testGroup "Compiler tests"
|
||||||
[ testCase "Forks" $ do
|
[ testCase "Forks" $ do
|
||||||
res <- liftIO $ evaluateFileResult "./test/fork.tri"
|
res <- liftIO $ evaluateFile "./test/fork.tri"
|
||||||
res @?= Fork Leaf Leaf
|
res @?= Fork Leaf Leaf
|
||||||
, testCase "File ends with comment" $ do
|
, testCase "File ends with comment" $ do
|
||||||
res <- liftIO $ evaluateFileResult "./test/comments-1.tri"
|
res <- liftIO $ evaluateFile "./test/comments-1.tri"
|
||||||
res @?= Fork (Stem Leaf) Leaf
|
res @?= Fork (Stem Leaf) Leaf
|
||||||
, testCase "Mapping and Equality" $ do
|
, testCase "Mapping and Equality" $ do
|
||||||
res <- liftIO $ evaluateFileResult "./test/map.tri"
|
res <- liftIO $ evaluateFile "./test/map.tri"
|
||||||
res @?= Stem Leaf
|
res @?= Stem Leaf
|
||||||
, testCase "Eval and decoding string" $ do
|
|
||||||
library <- liftIO $ evaluateFile "./lib/base.tri"
|
|
||||||
res <- liftIO $ evaluateFileWithContext library "./test/string.tri"
|
|
||||||
decodeResult (result res) @?= "\"String test!\""
|
|
||||||
]
|
]
|
||||||
|
|
||||||
propertyTests :: TestTree
|
propertyTests :: TestTree
|
||||||
|
@ -1 +0,0 @@
|
|||||||
x = t (t t) t
|
|
@ -1 +0,0 @@
|
|||||||
head (map (\i : lconcat "String " i) [("test!")])
|
|
20
tricu.cabal
20
tricu.cabal
@ -1,7 +1,7 @@
|
|||||||
cabal-version: 1.12
|
cabal-version: 1.12
|
||||||
|
|
||||||
name: tricu
|
name: tricu
|
||||||
version: 0.5.0
|
version: 0.4.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
|
||||||
@ -17,8 +17,15 @@ executable tricu
|
|||||||
hs-source-dirs:
|
hs-source-dirs:
|
||||||
src
|
src
|
||||||
default-extensions:
|
default-extensions:
|
||||||
|
ConstraintKinds
|
||||||
|
DataKinds
|
||||||
DeriveDataTypeable
|
DeriveDataTypeable
|
||||||
|
DeriveGeneric
|
||||||
|
FlexibleContexts
|
||||||
|
FlexibleInstances
|
||||||
|
GeneralizedNewtypeDeriving
|
||||||
OverloadedStrings
|
OverloadedStrings
|
||||||
|
ScopedTypeVariables
|
||||||
ghc-options: -threaded -rtsopts -with-rtsopts=-N -optl-pthread -fPIC
|
ghc-options: -threaded -rtsopts -with-rtsopts=-N -optl-pthread -fPIC
|
||||||
build-depends:
|
build-depends:
|
||||||
base >=4.7
|
base >=4.7
|
||||||
@ -27,11 +34,11 @@ executable tricu
|
|||||||
, haskeline
|
, haskeline
|
||||||
, megaparsec
|
, megaparsec
|
||||||
, mtl
|
, mtl
|
||||||
, text
|
|
||||||
other-modules:
|
other-modules:
|
||||||
|
Compiler
|
||||||
Eval
|
Eval
|
||||||
FileEval
|
|
||||||
Lexer
|
Lexer
|
||||||
|
Library
|
||||||
Parser
|
Parser
|
||||||
REPL
|
REPL
|
||||||
Research
|
Research
|
||||||
@ -41,9 +48,6 @@ test-suite tricu-tests
|
|||||||
type: exitcode-stdio-1.0
|
type: exitcode-stdio-1.0
|
||||||
main-is: Spec.hs
|
main-is: Spec.hs
|
||||||
hs-source-dirs: test, src
|
hs-source-dirs: test, src
|
||||||
default-extensions:
|
|
||||||
DeriveDataTypeable
|
|
||||||
OverloadedStrings
|
|
||||||
build-depends:
|
build-depends:
|
||||||
base
|
base
|
||||||
, cmdargs
|
, cmdargs
|
||||||
@ -54,12 +58,12 @@ test-suite tricu-tests
|
|||||||
, tasty
|
, tasty
|
||||||
, tasty-hunit
|
, tasty-hunit
|
||||||
, tasty-quickcheck
|
, tasty-quickcheck
|
||||||
, text
|
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
other-modules:
|
other-modules:
|
||||||
|
Compiler
|
||||||
Eval
|
Eval
|
||||||
FileEval
|
|
||||||
Lexer
|
Lexer
|
||||||
|
Library
|
||||||
Parser
|
Parser
|
||||||
REPL
|
REPL
|
||||||
Research
|
Research
|
||||||
|
Loading…
x
Reference in New Issue
Block a user