new-lang/checker/props.py

357 lines
9.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""
This module hosts propositions.
"""
from __future__ import annotations
from typing import Callable
from .terms import USABLE_VAR_NAMES, Const, Term, Var, ecsl
from dataclasses import dataclass
class Prop:
"""
A proposition is a logical statement. It can be proven by the proof
checker, or it can be used as a hypothesis to prove another
proposition.
"""
def __repr__(self) -> str:
"""
Create a representation of a proposition.
:return: String representation of the proposition.
:rtype: str
"""
return self.to_str(vars=set())
def contains(self, term : Term) -> bool:
"""
Determine whether a given term, value or variable is contained or
described by this proposition.
:param term: The term to look for.
:type term: Term
:return: Whether the term is contained in this proposition.
:rtype: bool
"""
return False
def normalize(self) -> Prop:
"""
Simplify the terms of a proposition.
"""
return self
def replace(self, old : Term, new : Term) -> Prop:
"""
Find any occurrence of a given term, and replace it with the new
term.
:param old: The term to replace.
:type old: Term
:param new: The term to replace it with.
:type new: Term
:return: The proposition with the term replaced.
:rtype: Prop
"""
return self
def to_str(self, vars : set[str]) -> str:
return "<undefined prop>"
@dataclass(frozen=True)
class And(Prop):
"""
The logical and-operator `P ∧ Q`.
"""
P : Prop
Q : Prop
def __repr__(self) -> str:
"""
Create a representation of a proposition.
:return: String representation of the proposition.
:rtype: str
"""
return self.to_str(vars=set())
def contains(self, term: Term) -> bool:
return self.P.contains(term) or self.Q.contains(term)
def normalize(self) -> Prop:
"""
Simplify the terms of a proposition.
"""
return And(P=self.P.normalize(), Q=self.Q.normalize())
def replace(self, old: Term, new: Term) -> Prop:
return And(
P=self.P.replace(old=old, new=new),
Q=self.Q.replace(old=old, new=new),
)
def to_str(self, vars: set[str]) -> str:
p_str = ecsl(self.P.to_str(vars=vars))
q_str = ecsl(self.Q.to_str(vars=vars))
return f"{p_str}{q_str}"
@dataclass(frozen=True)
class Eq(Prop):
"""
Compare whether two terms are equal.
"""
x : Term
y : Term
def __repr__(self) -> str:
"""
Create a representation of a proposition.
:return: String representation of the proposition.
:rtype: str
"""
return self.to_str(vars=set())
def contains(self, term: Term) -> bool:
return self.x.contains(term) or self.y.contains(term)
def normalize(self) -> Prop:
"""
Simplify the terms of a proposition.
"""
return Eq(x=self.x.normalize(), y=self.y.normalize())
def replace(self, old: Term, new: Term) -> Prop:
return Eq(
x=self.x.replace(old=old, new=new),
y=self.y.replace(old=old, new=new),
)
def to_str(self, vars : set[str]) -> str:
x_str = ecsl(self.x.to_str(vars={ key : Var() for key in vars }))
y_str = ecsl(self.y.to_str(vars={ key : Var() for key in vars }))
return f"{x_str} = {y_str}"
@dataclass(frozen=True)
class Exists(Prop):
"""
Create a statement that demonstrates there exists an x such that the
given statement is true.
"""
x : Callable[[Term], Prop]
def __repr__(self) -> str:
"""
Create a representation of a proposition.
:return: String representation of the proposition.
:rtype: str
"""
return self.to_str(vars=set())
def contains(self, term : Term) -> bool:
i, c = 0, Const("")
while i == 0 or term.contains(c):
i += 1
c = Const("temp_exists_variable_{i}")
return self.x(c).contains(term=term)
def normalize(self) -> Prop:
"""
Simplify the terms of a proposition.
"""
return Exists(lambda n : self.x(n).normalize())
def replace(self, old: Term, new: Term) -> Prop:
def replaced_func(input_term : Term) -> Prop:
c = unique_const(input_term=input_term)
return (
self.x(c)
.replace(old=old, new=new)
.replace(old=c, new=input_term)
)
def unique_const(input_term : Term) -> Term:
"""
Get a unique const value that doesn't interfere with either the
old term that needs to be replaced, nor with the input term.
"""
i, c = 0, Const("")
while i == 0 or input_term.contains(c) or old.contains(c):
i += 1
c = Const(f"temp_unique_value_{i}")
return c
return Exists(replaced_func)
def to_str(self, vars : set[str]) -> str:
for c in USABLE_VAR_NAMES:
if c in vars:
continue
vars.add(c)
s = self.x(Const(c)).to_str(vars=vars)
vars.remove(c)
return "" + c + "." + ecsl(s)
else:
raise ValueError(
"Ran out of usable var names!"
)
@dataclass(frozen=True)
class ForAll(Prop):
"""
Create a statement that is true for all x
"""
x : Callable[[Term], Prop]
def __repr__(self) -> str:
"""
Create a representation of a proposition.
:return: String representation of the proposition.
:rtype: str
"""
return self.to_str(vars=set())
def contains(self, term : Term) -> bool:
i, c = 0, Const("")
while i == 0 or term.contains(c):
i += 1
c = Const("temp_forall_variable_{i}")
return self.x(c).contains(term=term)
def normalize(self) -> Prop:
"""
Simplify the terms of a proposition.
"""
return ForAll(lambda n : self.x(n).normalize())
def replace(self, old: Term, new: Term) -> Prop:
def replaced_func(input_term : Term) -> Prop:
c = unique_const(input_term=input_term)
return (
self.x(c)
.replace(old=old, new=new)
.replace(old=c, new=input_term)
)
def unique_const(input_term : Term) -> Term:
"""
Get a unique const value that doesn't interfere with either the
old term that needs to be replaced, nor with the input term.
"""
i, c = 0, Const("")
while i == 0 or input_term.contains(c) or old.contains(c):
i += 1
c = Const(f"temp_unique_value_{i}")
return c
return ForAll(replaced_func)
def to_str(self, vars : set[str]) -> str:
for c in USABLE_VAR_NAMES:
if c in vars:
continue
vars.add(c)
s = self.x(Const(c)).to_str(vars=vars)
vars.remove(c)
return "" + c + "." + ecsl(s)
else:
raise ValueError(
"Ran out of usable var names!"
)
@dataclass(frozen=True)
class Implies(Prop):
"""
The logical operator `P -> Q`.
"""
P : Prop
Q : Prop
def __repr__(self) -> str:
"""
Create a representation of a proposition.
:return: String representation of the proposition.
:rtype: str
"""
return self.to_str(vars=set())
def contains(self, term: Term) -> bool:
return self.P.contains(term) or self.Q.contains(term)
def normalize(self) -> Prop:
"""
Simplify the terms of a proposition.
"""
return Implies(P=self.P.normalize(), Q=self.Q.normalize())
def replace(self, old: Term, new: Term) -> Prop:
return Implies(
P=self.P.replace(old=old, new=new),
Q=self.Q.replace(old=old, new=new),
)
def to_str(self, vars: set[str]) -> str:
p_str = ecsl(self.P.to_str(vars=vars))
q_str = ecsl(self.Q.to_str(vars=vars))
return f"{p_str}{q_str}"
@dataclass(frozen=True)
class Or(Prop):
"""
The logical and-operator `P v Q`.
"""
P : Prop
Q : Prop
def __repr__(self) -> str:
"""
Create a representation of a proposition.
:return: String representation of the proposition.
:rtype: str
"""
return self.to_str(vars=set())
def contains(self, term: Term) -> bool:
return self.P.contains(term) or self.Q.contains(term)
def normalize(self) -> Prop:
"""
Simplify the terms of a proposition.
"""
return Or(P=self.P.normalize(), Q=self.Q.normalize())
def replace(self, old: Term, new: Term) -> Prop:
return Or(
P=self.P.replace(old=old, new=new),
Q=self.Q.replace(old=old, new=new),
)
def to_str(self, vars: set[str]) -> str:
p_str = ecsl(self.P.to_str(vars=vars))
q_str = ecsl(self.Q.to_str(vars=vars))
return f"{p_str} {q_str}"