189 lines
4.9 KiB
Python
189 lines
4.9 KiB
Python
"""
|
|
Terms are values in an equation. They can be constants, variables,
|
|
functions, operators, and many more.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import copy
|
|
|
|
from dataclasses import dataclass
|
|
|
|
__known_consts : dict[str, Term] = {
|
|
}
|
|
|
|
def register_known_const(name : str, value : Term) -> None:
|
|
__known_consts[name] = value
|
|
|
|
class Term:
|
|
"""
|
|
Base class for all terms.
|
|
"""
|
|
|
|
def normalize(self) -> Term:
|
|
"""
|
|
Function to return a simplified version of the term.
|
|
"""
|
|
return self
|
|
|
|
def reduce_on_equality(self, other : Term) -> tuple[Term, Term]:
|
|
"""
|
|
Reduce parts that the two items are equal on.
|
|
"""
|
|
return self, other
|
|
|
|
def substitute(self, index : int, value : Term) -> Term:
|
|
"""
|
|
Substitute all variables of a given index number.
|
|
|
|
:param index: The variable's index.
|
|
:type index: int
|
|
:param value: The value to replace the variable with.
|
|
:type value:
|
|
"""
|
|
return self
|
|
|
|
@dataclass(frozen=True)
|
|
class App(Term):
|
|
"""
|
|
An App is the application of a function with an argument.
|
|
You could consider it the inverse of the lambda function.
|
|
"""
|
|
|
|
f : Term
|
|
x : Term
|
|
|
|
def normalize(self) -> Term:
|
|
f = self.f.normalize()
|
|
x = self.x.normalize()
|
|
|
|
default = App(f=f, x=x)
|
|
|
|
match f:
|
|
case App():
|
|
return default
|
|
|
|
case Const():
|
|
return default
|
|
|
|
case Lambda():
|
|
return f.resolve(x)
|
|
|
|
case Term():
|
|
return default
|
|
|
|
case Var():
|
|
return default
|
|
|
|
def reduce_on_equality(self, other: Term) -> tuple[Term, Term]:
|
|
if not isinstance(other, App):
|
|
return self, other
|
|
|
|
f1, f2 = self.f.normalize(), other.f.normalize()
|
|
x1, x2 = self.x.normalize(), other.x.normalize()
|
|
|
|
match ( f1 == f2, x1 == x2 ):
|
|
case ( True, True ):
|
|
return Term(), Term()
|
|
|
|
case ( True, False ):
|
|
return x1.reduce_on_equality(x2)
|
|
|
|
case ( False, True ):
|
|
return f1.reduce_on_equality(f2)
|
|
|
|
case ( False, False ):
|
|
return self, other
|
|
|
|
def substitute(self, index : int, value : Term) -> Term:
|
|
"""
|
|
Substitute all variables of a given index number.
|
|
|
|
:param index: The variable's index.
|
|
:type index: int
|
|
:param value: The value to replace the variable with.
|
|
:type value:
|
|
"""
|
|
return App(
|
|
f=self.f.substitute(index=index, value=value),
|
|
x=self.x.substitute(index=index, value=value),
|
|
)
|
|
|
|
@dataclass(frozen=True)
|
|
class Const(Term):
|
|
"""
|
|
Constants are 0-ary functions. They can represent variables, constants
|
|
or other values that generally take no input.
|
|
"""
|
|
|
|
name : str
|
|
|
|
def normalize(self) -> Term:
|
|
if self.name in __known_consts:
|
|
return copy.deepcopy(__known_consts.get(self.name, self))
|
|
else:
|
|
return self
|
|
|
|
@dataclass(frozen=True)
|
|
class Lambda(Term):
|
|
"""
|
|
Lambdas a nameless 1-ary functions.
|
|
"""
|
|
|
|
out : Term
|
|
|
|
def normalize(self) -> Term:
|
|
return Lambda(out=self.out.normalize())
|
|
|
|
def reduce_on_equality(self, other: Term) -> tuple[Term, Term]:
|
|
match other:
|
|
case Lambda():
|
|
return self.out, other.out
|
|
case _:
|
|
return self, other
|
|
|
|
def resolve(self, value : Term) -> Term:
|
|
"""
|
|
Resolve this lambda function by inserting a value.
|
|
|
|
:param value: The value to substitute.
|
|
"""
|
|
return self.substitute(index=-1, value=value)
|
|
|
|
def substitute(self, index : int, value : Term) -> Term:
|
|
"""
|
|
Substitute all variables of a given index number.
|
|
|
|
:param index: The variable's index.
|
|
:type index: int
|
|
:param value: The value to replace the variable with.
|
|
:type value:
|
|
"""
|
|
if index == -1:
|
|
# We're resolving this lambda function!
|
|
return self.out.substitute(index=index + 1, value=value)
|
|
else:
|
|
return Lambda(out=self.out.substitute(index=index + 1, value=value))
|
|
|
|
@dataclass(frozen=True)
|
|
class Var(Term):
|
|
"""
|
|
A variable is a value that can be substituted by a lambda function.
|
|
"""
|
|
|
|
index : int
|
|
|
|
def substitute(self, index : int, value : Term) -> Term:
|
|
"""
|
|
Substitute all variables of a given index number.
|
|
|
|
:param index: The variable's index.
|
|
:type index: int
|
|
:param value: The value to replace the variable with.
|
|
:type value:
|
|
"""
|
|
if self.index == index:
|
|
return copy.deepcopy(value)
|
|
else:
|
|
return self
|