Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verify definitions #116

Open
wants to merge 36 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
66383c8
Add function to export theorem dependencies in GraphML format
tirix Jun 4, 2023
efd0db3
Remove unwanted file!
tirix Jun 4, 2023
2d388c2
Fix typo
tirix Jun 4, 2023
43b89d5
Skipping syntactic dependencies
tirix Jun 4, 2023
414fdd7
Definition check and definition graph export
tirix Jun 8, 2023
293fd6e
Clippy and more one more check
tirix Jun 8, 2023
5facc9d
Fmt
tirix Jun 8, 2023
91ed49f
Error handling
tirix Jun 8, 2023
bc0cd3a
Special malformed definition case
tirix Jun 8, 2023
4212498
Clppy
tirix Jun 8, 2023
7fdcbab
organizing the parts that have been done so far
digama0 Jun 10, 2023
1ce90bc
Remove unused diagnostic type
tirix Jun 11, 2023
b0e7f20
refactoring
digama0 Jun 12, 2023
4005a02
check that statements don't use pending defs
digama0 Jun 12, 2023
7e64ac3
check refl, symm, trans
digama0 Jun 12, 2023
fc812fd
Misc comments
tirix Jun 12, 2023
1dbb11a
starting on justification processing
digama0 Jun 12, 2023
eb7f27a
Mention the possible missing definition case
tirix Jun 12, 2023
ecfd2e9
Tests for the definition checker
tirix Jun 12, 2023
7a4a68c
Format
tirix Jun 12, 2023
ec62775
Oops
tirix Jun 12, 2023
8a5f967
Merge branch 'main' into verify_definitions
tirix Jun 12, 2023
d1cf04a
Typo
tirix Jun 12, 2023
b0ec8cc
add free dummy check
digama0 Jun 12, 2023
43c4f60
DV condition for justifications
digama0 Jun 13, 2023
5823b9c
parameter DV check
digama0 Jun 13, 2023
2cd7866
dummy DV check
digama0 Jun 13, 2023
c685f4f
check for definiendum on RHS
digama0 Jun 13, 2023
57a8bca
misnamed axiom/def warnings
digama0 Jun 13, 2023
aa1eae3
Merge branch 'main' into verify_definitions
tirix Jun 22, 2023
d21c13b
Merge branch 'main' into verify_definitions
tirix Jun 22, 2023
1b77b7a
Merge branch 'main' into verify_definitions
tirix Jul 2, 2023
aa4cafc
Merge branch 'main' into verify_definitions
tirix Nov 27, 2023
7011aef
Fix merge
tirix Nov 27, 2023
80490fe
Merge branch 'main' into verify_definitions
digama0 Nov 27, 2023
ca1e4af
Merge branch 'main' into verify_definitions
tirix Jun 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 41 additions & 56 deletions src/defck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ pub struct DefResult {
impl DefResult {
/// Returns the definition axiom for the given syntax axiom.
#[must_use]
pub fn definition_for(&self, syntax_axiom: Label) -> Option<&Label> {
self.def_map.get(&syntax_axiom)
pub fn definition_for(&self, syntax_axiom: Label) -> Option<Label> {
self.def_map.get(&syntax_axiom).copied()
}

/// Returns the list of errors that were generated during the definition check pass.
Expand All @@ -54,44 +54,30 @@ impl Database {
// Parses the 'equality', 'primitive', and 'justification' commmands in the database,
// and store the result in the database for future fast access.
fn parse_equality_commands(&self, sset: &SegmentSet, definitions: &mut DefResult) {
let nset = self.name_result();
for sref in sset.segments(..) {
let buf = &**sref.buffer;
for (_, (_, command)) in &sref.j_commands {
use CommandToken::*;
match &**command {
[Keyword(cmd), label, Keyword(from), _reflexivity_law, _commutativity_law, _transitivity_law]
[Keyword(cmd), label, Keyword(from), _reflexivity_law, _symmetry_law, _transitivity_law]
if cmd.as_ref(buf) == b"equality" && from.as_ref(buf) == b"from" =>
{
let equality = self
.name_result()
.lookup_label(label.value(buf))
.unwrap()
.atom;
let equality = nset.lookup_label(label.value(buf)).unwrap().atom;
// TODO check it's a valid equality
definitions.equalities.push(equality);
}
[Keyword(cmd), ..] if cmd.as_ref(buf) == b"primitive" => {
for label in &command[1..] {
let primitive = self
.name_result()
.lookup_label(label.value(buf))
.unwrap()
.atom;
[Keyword(cmd), rest @ ..] if cmd.as_ref(buf) == b"primitive" => {
for label in rest {
let primitive = nset.lookup_label(label.value(buf)).unwrap().atom;
definitions.primitives.push(primitive);
}
}
[Keyword(cmd), justif_label, Keyword(for_), label]
if cmd.as_ref(buf) == b"justification" && for_.as_ref(buf) == b"for" =>
{
let theorem = self
.name_result()
.lookup_label(justif_label.value(buf))
.unwrap()
.atom;
let definition = self
.name_result()
.lookup_label(label.value(buf))
.unwrap()
.atom;
let theorem = nset.lookup_label(justif_label.value(buf)).unwrap().atom;
let definition = nset.lookup_label(label.value(buf)).unwrap().atom;
definitions.justifications.insert(definition, theorem);
}
_ => {}
Expand Down Expand Up @@ -138,6 +124,8 @@ impl Database {
if pending_definitions.len() == 1 {
let syntax_axiom = pending_definitions.remove(0);

// TODO check that the justification matches the definition

// Store the validated definition
definitions.definitions.insert(definition);
definitions.def_map.insert(syntax_axiom, definition);
Expand All @@ -154,40 +142,45 @@ impl Database {
.stmt_parse_result()
.get_formula(&sref)
.ok_or_else(|| Diagnostic::UnknownLabel(sref.label_span()))?;
let equality = &fmla.get_by_path(&[]).unwrap();
if !definitions.equalities.contains(equality) {
let root = fmla.root(self);
let equality = root.label();
if !definitions.equalities.contains(&equality) {
Err(Diagnostic::DefCkNotAnEquality(
names.atom_name(*equality).into(),
names.atom_name(equality).into(),
definitions
.equalities
.iter()
.map(|label| names.atom_name(*label).into())
.map(|&label| names.atom_name(label).into())
.collect(),
))
} else {
let Some(lhs) = root.nth_child(0) else {
return Err(Diagnostic::DefCkMalformedDefinition)
};

let syntax_axiom = lhs.label();
// push the definition to the validated list early, so that later definitions
// aren't as messed up if this check fails
definitions.definitions.insert(definition);
if let Some(prev) = definitions.def_map.insert(syntax_axiom, definition) {
return Err(Diagnostic::DefCkDuplicateDefinition(
names.atom_name(syntax_axiom).into(),
names.lookup_label_by_atom(prev).address,
));
}

// Remove the definition from the pending list
let syntax_axiom = &fmla.get_by_path(&[0]).unwrap();
let result = if let Some(pending_index) =
pending_definitions.iter().position(|x| *x == *syntax_axiom)
if let Some(pending_index) =
pending_definitions.iter().position(|&x| x == syntax_axiom)
{
pending_definitions.swap_remove(pending_index);
Ok(())
} else if let Some(previous_saddr) = definitions
.definition_for(*syntax_axiom)
.map(|a| names.lookup_label_by_atom(*a).address)
{
Err(Diagnostic::DefCkDuplicateDefinition(
names.atom_name(*syntax_axiom).into(),
previous_saddr,
))
} else {
Err(Diagnostic::DefCkMalformedDefinition)
};
return Err(Diagnostic::DefCkMalformedDefinition);
}

// Store the validated definition
definitions.definitions.insert(definition);
definitions.def_map.insert(*syntax_axiom, definition);
result
// TODO definition check

Ok(())
}
}
}
Expand All @@ -198,15 +191,7 @@ impl Database {
self.parse_equality_commands(sset, definitions);
let names = self.name_result();

// Fail the whole check if no equality has been defined
#[allow(clippy::manual_assert)]
if definitions.equalities.is_empty() {
definitions
.diagnostics
.insert(StatementAddress::default(), Diagnostic::DefCkNoEquality);
}

// TODO verify that the reflexivity, associativity, and transivity laws are well-formed
tirix marked this conversation as resolved.
Show resolved Hide resolved
// TODO verify that the reflexivity, symmetry, and transitivity laws are well-formed

let mut pending_definitions = vec![];
for sref in self
Expand Down
2 changes: 1 addition & 1 deletion src/export_deps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ impl Database {
if let Some(definition_axiom) =
self.definitions_result().definition_for(syntax_axiom)
{
let def_label = self.name_result().atom_name(*definition_axiom);
let def_label = self.name_result().atom_name(definition_axiom);
writer.write(
XmlEvent::start_element("edge")
.attr("source", as_str(label))
Expand Down
80 changes: 73 additions & 7 deletions src/formula.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,16 @@ impl Formula {
FormulaRef { db, formula: self }
}

/// Augment a formula with a database reference, to produce a [`FormulaRef`].
/// The resulting object implements [`Display`], [`Debug`], and [`IntoIterator`].
#[must_use]
pub const fn root<'a>(&'a self, db: &'a Database) -> SubFormulaRef<'a> {
SubFormulaRef {
node_id: self.root,
f_ref: self.as_ref(db),
}
}

/// Debug only, dumps the internal structure of the formula.
pub fn dump(&self, nset: &Nameset) {
println!(" Root: {}", self.root);
Expand All @@ -223,7 +233,8 @@ impl Formula {

/// Returns the label obtained when following the given path.
/// Each element of the path gives the index of the child to retrieve.
/// For example, the empty
/// For example, the empty path gives the root, and the path `[0, 2]`
/// in `foo(bar(a, b, baz()), d)` gives `baz`.
#[must_use]
pub fn get_by_path(&self, path: &[usize]) -> Option<Label> {
let mut node_id = self.root;
Expand Down Expand Up @@ -462,7 +473,7 @@ impl<'a> Iterator for LabelIter<'a> {
}

/// A [`Formula`] reference in the context of a [`Database`].
/// This allows the values in the [`Formula`] to be resolved,
/// This allows the values in the [`Formula`] to be resolved.
#[derive(Copy, Clone)]
pub struct FormulaRef<'a> {
db: &'a Database,
Expand All @@ -478,8 +489,8 @@ impl<'a> std::ops::Deref for FormulaRef<'a> {
}

impl<'a> FormulaRef<'a> {
/// Convert the formula back to a flat list of symbols
/// This is slow and shall not normally be called except for showing a result to the user.
/// Convert the formula back to a flat list of symbols.
/// This is slow and should not normally be called except for showing a result to the user.
#[must_use]
pub(crate) fn iter(self) -> Flatten<'a> {
let mut f = Flatten {
Expand All @@ -493,7 +504,7 @@ impl<'a> FormulaRef<'a> {
}

/// Returns a copy of this formula with a new root
/// (in the same tree)
/// (in the same tree).
fn to_rerooted(self, new_root: NodeId) -> Formula {
Formula {
root: new_root,
Expand All @@ -504,7 +515,7 @@ impl<'a> FormulaRef<'a> {
}

/// Computes the typecode of the given node
/// according to the corresponding statement
/// according to the corresponding statement.
fn compute_typecode_at(&self, node_id: NodeId) -> TypeCode {
self.db.label_typecode(self.formula.tree[node_id])
}
Expand Down Expand Up @@ -652,7 +663,9 @@ impl<'a> IntoIterator for FormulaRef<'a> {
}
}

struct SubFormulaRef<'a> {
/// A reference to a subformula, usable for tree traversals.
#[derive(Copy, Clone)]
pub struct SubFormulaRef<'a> {
node_id: NodeId,
f_ref: FormulaRef<'a>,
}
Expand All @@ -679,6 +692,59 @@ impl<'a> Debug for SubFormulaRef<'a> {
}
}

impl<'a> SubFormulaRef<'a> {
/// Returns the statement label applied at this tree node.
#[must_use]
pub fn label(&self) -> Label {
self.f_ref.formula.tree[self.node_id]
}

/// Returns an iterator over the list of children of this node.
pub fn children(&self) -> SubFormulaChildren<'a> {
SubFormulaChildren {
iter: self.f_ref.formula.tree.children_iter(self.node_id),
f_ref: self.f_ref,
}
}

/// Returns the `n`th child of this node.
#[must_use]
pub fn nth_child(&self, index: usize) -> Option<Self> {
self.children().nth(index)
}

/// Returns a reference to the subtree obtained when following the given path.
/// Each element of the path gives the index of the child to retrieve.
/// For example, the empty path gives the root, and the path `[0, 2]`
/// in `foo(bar(a, b, baz(c)), d)` gives `baz(c)`.
#[must_use]
pub fn get_by_path(mut self, path: &[usize]) -> Option<Self> {
for &index in path {
self = self.nth_child(index)?;
}
Some(self)
}
}

/// An iterator over the children of a node.
#[must_use]
#[derive(Debug)]
pub struct SubFormulaChildren<'a> {
iter: SiblingIter<'a, Label>,
f_ref: FormulaRef<'a>,
}

impl<'a> Iterator for SubFormulaChildren<'a> {
type Item = SubFormulaRef<'a>;

fn next(&mut self) -> Option<Self::Item> {
Some(SubFormulaRef {
node_id: self.iter.next()?,
f_ref: self.f_ref,
})
}
}

/// An iterator going through each symbol in a formula
#[derive(Debug)]
pub struct Flatten<'a> {
Expand Down