Skip to content

Commit 3a894f2

Browse files
committed
Add transformation to applicative terms and enhance the UI
1 parent a577cea commit 3a894f2

14 files changed

+245
-134
lines changed

README.md

+16-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
# lambda-parser
2-
Parser for lambda terms, written in Haskell, that is also able to apply substitution, according to the rules of lambda calculus
2+
Parser for lambda terms, written in Haskell, that is also able to execute a list of operations on the terms
33

44
## Table of contents
55
- [Prerequisites](#prerequisites)
66
- [Running the project](#running-the-project)
77
- [Installing the executable manually](#installing-the-executable-manually)
88
- [Documentation](#documentation)
99
- [Grammar](#grammar)
10-
- [Substitution](#substitution)
10+
- [Supported operations](#supported-operations)
11+
- [Substitution](#substitution)
12+
- [Transformation to applicative term](#transformation-to-applicative-term)
13+
- [Counting beta-redexes](#counting-beta-redexes)
1114
- [License](#license)
1215
- [Contacts](#contacts)
1316

@@ -35,7 +38,7 @@ cabal install
3538
Now the executable should be located in the configured `installdir` of `cabal` (usually `~/.local/bin` or `~/.cabal/bin`).
3639

3740
## Documentation
38-
The parser traverses the lambda term and creates AST where the nodes are variables, applications and abstractions (lexing and parsing are done simultaneously). Then the substitution is done on the AST with the corresponding tokens instead of directly on the term. After the substitution, a new lambda term is generated based on the newly created AST and the result is returned as the final answer. If there are no brackets, the application term is left associative. Bear in mind that in the generated final term some brackets may be added because there is no way to know where they can be skipped without looking ahead, which the current generator does not do.
41+
The parser traverses the lambda term and creates AST where the nodes are variables, applications and abstractions (lexing and parsing are done simultaneously). Then the operations are done on the AST with the corresponding tokens instead of directly on the term. After the operation, a new lambda term is generated based on the newly created AST and the result is returned as the final answer. If there are no brackets, the application term is left associative. Bear in mind that in the generated final term some brackets may be added because there is no way to know where they can be skipped without looking ahead, which the current generator does not do.
3942

4043
### Grammar
4144
$` variable \Coloneqq [a-z] `$ <br>
@@ -45,11 +48,20 @@ $` \quad \quad \quad \quad \ \ | \quad λ \ \langle variable\rangle \ . \ \lan
4548

4649
(Here $` (\!( \ )\!)^* `$ means that the brackets themselves are optional)
4750

48-
### Substitution
51+
### Supported operations
52+
This is the list of currently supported operations, that can be executed on the terms after parsing:
53+
54+
#### Substitution
4955
There are currently 2 supported versions for the substitutions:
5056
- curry substitution - this is the standart substitution, done directly on the original term. In order for it to be correct and not change the semantics of the lambda terms, in the cases where a naive substitution would cause a problem, the bound variables are renamed. The algorithm starts by trying all letters, starting from *a*, until a letter is found that would not cause the term to change its semantics and renames the bound variable with the found letter. If a suitable letter is not found, then the algorithm continues to search for any suitable character (which does not follow the grammar);
5157
- nameless term substitution - in order for this substitution to be applied the term is first transformed into nameless term. Nameless terms don't have variable names - instead de Bruijn indexes are used to denote which variables are bound and which - free. The substitution is then done on the indexes instead of the variables by utilizing the `shift` function. After the substitution is done, the term is transformed back into its named version, where the bound variables may have different names (which does not change the semantics of the term).
5258

59+
#### Transformation to applicative term
60+
The transformation to applicative term is non-deterministic operation because there are 2 approaches to it - either to start from the outside or the inside. The algorithm always starts from the outside, except in the case where starting from the outside would not lead to the full transformation - this is the case where there are 2 nested abstractions where the argument of the first is part of the free variables of the second. When this case is encountered the transformation is applied to the inner abstraction first and after it is done the partial result is transformed again to achieve the coorect result.
61+
62+
#### Counting beta-redexes
63+
Counting the beta-redexes in s single term is the simplest operation. The algorithm traverses the AST and counts the occurences of applications where the first argument is abstraction.
64+
5365
## License
5466
The project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for more information.
5567

lambda-parser.cabal

+4-2
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,14 @@ executable lambda-parser
3131
Examples.Terms
3232
Interaction
3333
Parser
34-
Transformer
35-
Generator
34+
Transformers
35+
Generators
3636
Substitution.Named
3737
Substitution.Nameless
3838
Libs.Stack
3939
Libs.BetaRedexes
40+
Utils.Variables
41+
Terms
4042

4143
-- LANGUAGE extensions used by modules in this package.
4244
-- other-extensions:

src/Generator.hs

-31
This file was deleted.

src/Generators.hs

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
module Generators where
2+
3+
import Terms (Term (..), ApplicativeTerm (..), Combinator (..), NamelessTerm (..))
4+
5+
generateTerm :: Term -> String
6+
generateTerm (Variable var) = [var]
7+
generateTerm (Application lhs rhs) =
8+
let generatedLhs = case lhs of
9+
var@(Variable _) -> generateTerm var
10+
application@(Application _ _) -> generateTerm application
11+
abstraction@(Abstraction _ _) -> '(' : generateTerm abstraction ++ [')']
12+
generatedRhs = case rhs of
13+
var@(Variable _) -> generateTerm var
14+
application@(Application _ _) -> '(' : generateTerm application ++ [')']
15+
abstraction@(Abstraction _ _) -> '(' : generateTerm abstraction ++ [')']
16+
in generatedLhs ++ generatedRhs
17+
generateTerm (Abstraction argument body) = 'λ' : argument : '.' : generateTerm body
18+
19+
generateNamelessTerm :: NamelessTerm -> String
20+
generateNamelessTerm (NamelessVariable var) = show var
21+
generateNamelessTerm (NamelessApplication lhs rhs) =
22+
let generatedLhs = case lhs of
23+
var@(NamelessVariable _) -> generateNamelessTerm var
24+
application@(NamelessApplication _ _) -> generateNamelessTerm application
25+
abstraction@(NamelessAbstraction _) -> '(' : generateNamelessTerm abstraction ++ [')']
26+
generatedRhs = case rhs of
27+
var@(NamelessVariable _) -> generateNamelessTerm var
28+
application@(NamelessApplication _ _) -> '(' : generateNamelessTerm application ++ [')']
29+
abstraction@(NamelessAbstraction _) -> '(' : generateNamelessTerm abstraction ++ [')']
30+
in generatedLhs ++ generatedRhs
31+
generateNamelessTerm (NamelessAbstraction body) = 'λ' : generateNamelessTerm body
32+
33+
generateCombinator :: Combinator -> String
34+
generateCombinator KCombinator = "K"
35+
generateCombinator SCombinator = "S"
36+
37+
generateApplicativeTerm :: ApplicativeTerm -> String
38+
generateApplicativeTerm (ApplicativeVariable var) = [var]
39+
generateApplicativeTerm (ApplicativeApplication lhs rhs) =
40+
let generatedLhs = generateApplicativeTerm lhs
41+
generatedRhs = case rhs of
42+
var@(ApplicativeVariable _) -> generateApplicativeTerm var
43+
application@(ApplicativeApplication _ _) -> '(' : generateApplicativeTerm application ++ [')']
44+
combinator@(ApplicativeCombinator _) -> generateApplicativeTerm combinator
45+
in generatedLhs ++ generatedRhs
46+
generateApplicativeTerm (ApplicativeCombinator combinator) = generateCombinator combinator

src/Interaction.hs

+33-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
module Interaction where
22

33
import Prelude hiding (lookup)
4-
import Generator (generateTerm, generateNamelessTerm)
4+
import Generators (generateTerm, generateNamelessTerm, generateApplicativeTerm)
55
import Parser (parseTerm)
66
import Libs.Stack (emptyStack)
77
import Substitution.Named (substitute)
8-
import Transformer (toNameless, toNamed)
8+
import Transformers (toNameless, toNamed, toApplicative)
99
import Data.Map (empty, lookup)
1010
import Substitution.Nameless (substituteNameless)
11+
import Libs.BetaRedexes (extractBetaRedexes)
12+
13+
requestMode :: IO Int
14+
requestMode = do
15+
putStrLn "Please choose what kind of operation you want to do (1, 2, 3):"
16+
putStrLn "1) Substitution"
17+
putStrLn "2) Transformation from λ-term to Applicative term"
18+
putStrLn "3) Count beta-redexes in λ-term" >> read <$> getLine
1119

1220
data TermType = Nameless | Named deriving (Show, Eq)
1321

@@ -39,3 +47,26 @@ doSubstituteNameless term var substituteTerm outputTermType =
3947
in if outputTermType == Named
4048
then generateTerm $ toNamed result updatedContext
4149
else generateNamelessTerm result
50+
51+
executeSubstitution :: IO ()
52+
executeSubstitution = do
53+
term <- putStr "Input term: " >> getLine
54+
var <- putStr "Input variable to substitute: " >> getLine -- not using getChr intentionally
55+
if length var /= 1
56+
then error "please provide valid variable"
57+
else do
58+
substituteTerm <- putStr "Input substitution term: " >> getLine
59+
substitutionType <- getSubstitutionType
60+
if substitutionType == Named
61+
then putStrLn $ doSubstituteNamed term var substituteTerm
62+
else do
63+
outputFormat <- getOutputTermType
64+
putStrLn $ doSubstituteNameless term var substituteTerm outputFormat
65+
66+
executeTransformationToApplicative :: IO ()
67+
executeTransformationToApplicative =
68+
putStr "Input term: " >> flip parseTerm emptyStack <$> getLine >>= putStrLn . generateApplicativeTerm . toApplicative
69+
70+
executeBetaRedexesCount :: IO ()
71+
executeBetaRedexesCount =
72+
putStr "Input term: " >> flip parseTerm emptyStack <$> getLine >>= mapM_ (putStrLn . generateTerm) . extractBetaRedexes

src/Libs/BetaRedexes.hs

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
module Libs.BetaRedexes where
2-
import Parser (Term (Variable, Application, Abstraction))
3-
import Generator (generateTerm)
2+
import Terms (Term (..))
3+
import Generators (generateTerm)
44

55
extractBetaRedexes :: Term -> [Term]
66
extractBetaRedexes (Variable _) = []
77
extractBetaRedexes term@(Application lhs@(Abstraction _ body) rhs) =
88
term : extractBetaRedexes body ++ extractBetaRedexes rhs
99
extractBetaRedexes (Application lhs rhs) =
1010
extractBetaRedexes lhs ++ extractBetaRedexes rhs
11-
extractBetaRedexes (Abstraction _ body) = extractBetaRedexes body
12-
13-
displayBetaRedexes :: Term -> IO ()
14-
displayBetaRedexes term =
15-
mapM_ (putStrLn . generateTerm) $ extractBetaRedexes term
11+
extractBetaRedexes (Abstraction _ body) = extractBetaRedexes body

src/Main.hs

+11-13
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
module Main where
22

33
import System.IO (BufferMode (BlockBuffering), hSetBuffering, stdout)
4-
import Interaction (getSubstitutionType, getOutputTermType, doSubstituteNameless, TermType (Named), doSubstituteNamed)
4+
import Interaction (
5+
requestMode,
6+
executeSubstitution,
7+
executeTransformationToApplicative,
8+
executeBetaRedexesCount)
59

610
main :: IO ()
711
main = do
812
hSetBuffering stdout $ BlockBuffering $ Just 1
9-
term <- putStr "Input term: " >> getLine
10-
var <- putStr "Input variable to substitute: " >> getLine -- not using getChr intentionally
11-
if length var /= 1
12-
then error "please provide valid variable"
13-
else do
14-
substituteTerm <- putStr "Input substitution term: " >> getLine
15-
substitutionType <- getSubstitutionType
16-
if substitutionType == Named
17-
then putStrLn $ doSubstituteNamed term var substituteTerm
18-
else do
19-
outputFormat <- getOutputTermType
20-
putStrLn $ doSubstituteNameless term var substituteTerm outputFormat
13+
modeIndex <- requestMode
14+
case modeIndex of
15+
1 -> executeSubstitution
16+
2 -> executeTransformationToApplicative
17+
3 -> executeBetaRedexesCount
18+
_ -> putStrLn "Unrecognized operation!" >> main

src/Parser.hs

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
1-
module Parser (parseTerm, Term(Variable, Application, Abstraction)) where
1+
module Parser (parseTerm) where
22

33
import Data.Char (isAlpha)
44
import Libs.Stack (Stack, peek, pop, emptyStack, push)
5-
6-
data Term =
7-
Variable Char |
8-
Application Term Term |
9-
Abstraction Char Term
10-
deriving (Show)
5+
import Terms (Term (..))
116

127
takeWhileClosingBracket :: String -> Int-> String
138
takeWhileClosingBracket ('(':xs) count = '(':takeWhileClosingBracket xs (count + 1)

src/Substitution/Named.hs

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
module Substitution.Named where
22

3-
import Parser (Term (Application, Variable, Abstraction))
3+
import Terms (Term (Application, Variable, Abstraction))
44
import Data.Char (chr, ord)
5-
6-
freeVariables :: Term -> [Char]
7-
freeVariables (Variable x) = [x]
8-
freeVariables (Application lhs rhs) = freeVariables lhs ++ freeVariables rhs
9-
freeVariables (Abstraction argument body) = filter (/= argument) $ freeVariables body
5+
import Utils.Variables (freeVariables)
106

117
chooseUniqueVariable :: Term -> Term -> Char
128
chooseUniqueVariable first second = let vars = freeVariables first ++ freeVariables second

src/Substitution/Nameless.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module Substitution.Nameless where
2-
import Transformer (NamelessTerm (NamelessVariable, NamelessApplication, NamelessAbstraction), NamingContext)
2+
import Terms (NamelessTerm (..))
33

44
shift :: Int -> Int -> NamelessTerm -> NamelessTerm
55
shift c d var@(NamelessVariable k)

src/Terms.hs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module Terms where
2+
3+
data Term =
4+
Variable Char |
5+
Application Term Term |
6+
Abstraction Char Term
7+
deriving (Show, Eq)
8+
9+
data NamelessTerm
10+
= NamelessVariable Int
11+
| NamelessApplication NamelessTerm NamelessTerm
12+
| NamelessAbstraction NamelessTerm
13+
deriving (Show)
14+
15+
data Combinator = KCombinator | SCombinator deriving (Eq)
16+
17+
data ApplicativeTerm
18+
= ApplicativeVariable Char
19+
| ApplicativeApplication ApplicativeTerm ApplicativeTerm
20+
| ApplicativeCombinator Combinator
21+
deriving (Eq)

src/Transformer.hs

-61
This file was deleted.

0 commit comments

Comments
 (0)