Skip to content

Commit

Permalink
added messages for free-variables
Browse files Browse the repository at this point in the history
  • Loading branch information
qbixxx committed Oct 1, 2024
1 parent 1789108 commit 4ab042f
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 8 deletions.
7 changes: 3 additions & 4 deletions src/main/scala/LambdaInterpreter.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import scala.io.StdIn.readLine

object LambdaInterpreter {
Expand Down Expand Up @@ -36,10 +35,10 @@ object LambdaInterpreter {
val reducedExp = Parser.buildWithAST(reducedAst)
println(reducedExp)
case FreeVariables =>
val freeVariables = Reducer.freeVariables(ast)
println(freeVariables)
// Only print free variables when mode is set to FreeVariables
Reducer.printFreeVariables(ast)
}
}
main()
}
}
}
46 changes: 42 additions & 4 deletions src/main/scala/Reductor.scala → src/main/scala/Reducer.scala
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
object Reducer {

// ANSI escape codes for colors (you can disable them if not supported)
val RED = "\u001b[31m"
val GREEN = "\u001b[32m"
val YELLOW = "\u001b[33m"
val BLUE = "\u001b[34m"
val RESET = "\u001b[0m"

// Alpha conversion - renaming bound variables to avoid collision
private def alphaConversion(ast: AST, oldName: String, newName: String): AST = ast match {
case Variable(name) if name == oldName => Variable(newName)
case Variable(name) if name == oldName =>
println(s"${YELLOW}Alpha conversion: renaming $oldName to $newName${RESET}")
Variable(newName)
case Variable(name) => Variable(name)
case Abstraction(Variable(param), body) if param == oldName =>
println(s"${YELLOW}Alpha conversion: renaming $param to $newName${RESET}")
Abstraction(Variable(newName), alphaConversion(body, oldName, newName))
case Abstraction(Variable(param), body) =>
if (freeVariables(body).contains(newName)) {
val newParam = newName + "*"
println(s"${YELLOW}Alpha conversion: renaming $newName to avoid collision, new name: $newParam${RESET}")
Abstraction(Variable(newParam), alphaConversion(body, oldName, newParam))
} else {
Abstraction(Variable(param), alphaConversion(body, oldName, newName))
Expand All @@ -15,13 +28,15 @@ object Reducer {
Application(alphaConversion(func, oldName, newName), alphaConversion(arg, oldName, newName))
}

// Substitution - replaces the variable with the argument in the body
private def substitute(ast: AST, param: String, replacement: AST): AST = ast match {
case Variable(name) if name == param => replacement
case Variable(name) => Variable(name)
case Abstraction(Variable(name), body) if name == param => Abstraction(Variable(name), body)
case Abstraction(Variable(name), body) =>
if (freeVariables(replacement).contains(name)) {
val newName = name + "*"
println(s"${YELLOW}Alpha conversion needed during substitution: renaming $name to $newName${RESET}")
val renamedBody = alphaConversion(body, name, newName)
Abstraction(Variable(newName), substitute(renamedBody, param, replacement))
} else {
Expand All @@ -31,20 +46,29 @@ object Reducer {
Application(substitute(func, param, replacement), substitute(arg, param, replacement))
}

// Beta reduction
private def betaReduce(ast: AST): AST = ast match {
case Application(Abstraction(Variable(name), body), arg) =>
println(s"${GREEN}Beta reduction: applying argument ${Parser.buildWithAST(arg)} to function λ$name.${Parser.buildWithAST(body)}${RESET}")
if (freeVariables(arg).contains(name)) {
val newName = name + "*"
val renamedBody = alphaConversion(body, name, newName)
substitute(renamedBody, newName, arg)
println(s"${GREEN}After alpha conversion: ${Parser.buildWithAST(renamedBody)}${RESET}")
val result = substitute(renamedBody, newName, arg)
println(s"${GREEN}After beta reduction: ${Parser.buildWithAST(result)}${RESET}")
result
} else {
substitute(body, name, arg)
val result = substitute(body, name, arg)
println(s"${GREEN}After beta reduction: ${Parser.buildWithAST(result)}${RESET}")
result
}
case _ => ast
}

// Call-by-name reduction
def callByName(ast: AST): AST = ast match {
case Application(Abstraction(Variable(param), body), arg) =>
println(s"${RED}Reducing (λ$param.${Parser.buildWithAST(body)} ${Parser.buildWithAST(arg)}) by Call-by-Name${RESET}")
callByName(betaReduce(Application(Abstraction(Variable(param), body), arg)))
case Application(func, arg) =>
val reducedFunc = callByName(func)
Expand All @@ -58,8 +82,10 @@ object Reducer {
case Variable(name) => Variable(name)
}

// Call-by-value reduction
def callByValue(ast: AST): AST = ast match {
case Application(func, arg) =>
println(s"${RED}Reducing (Application ${Parser.buildWithAST(func)} ${Parser.buildWithAST(arg)}) by Call-by-Value${RESET}")
val reducedFunc = callByValue(func)
val reducedArg = callByValue(arg)
reducedFunc match {
Expand All @@ -72,13 +98,25 @@ object Reducer {
case Variable(name) => Variable(name)
}

// Finding free variables in the expression
def freeVariables(ast: AST): Set[String] = ast match {
case Variable(name) => Set(name)
case Abstraction(Variable(param), body) => freeVariables(body) - param
case Application(func, arg) => freeVariables(func) ++ freeVariables(arg)
}

// Print free variables of the expression (only called when mode is set to FreeVariables)
def printFreeVariables(ast: AST): Unit = {
val freeVars = freeVariables(ast)
if (freeVars.nonEmpty) {
println(s"${BLUE}Free variables: ${freeVars.mkString(", ")}${RESET}")
} else {
println(s"${BLUE}No free variables found${RESET}")
}
}
}

// Function to generate unique names to avoid collisions
private def generateUniqueName(base: String, usedNames: Set[String]): String = {
var newName = base + "*"
var counter = 1
Expand All @@ -87,4 +125,4 @@ private def generateUniqueName(base: String, usedNames: Set[String]): String = {
counter += 1
}
newName
}
}

0 comments on commit 4ab042f

Please sign in to comment.