]> Repositorios git - scryer-prolog.git/commitdiff
Differentiate anonymous variables
authorbakaq <[email protected]>
Thu, 15 Aug 2024 07:55:20 +0000 (04:55 -0300)
committerbakaq <[email protected]>
Thu, 15 Aug 2024 23:08:29 +0000 (20:08 -0300)
src/machine/lib_machine.rs
src/machine/parsed_results.rs

index c42bcae03bef6ca5b552b71e906e7ff10fbb5110..ec264ed4cefda36a0d1f4b43310f4ca4fcfa8370 100644 (file)
@@ -34,7 +34,7 @@ impl Iterator for QueryState<'_> {
     type Item = Result<QueryResolutionLine, String>;
 
     fn next(&mut self) -> Option<Self::Item> {
-        let var_names = &self.var_names;
+        let var_names = &mut self.var_names;
         let term_write_result = &self.term;
         let machine = &mut self.machine;
 
@@ -91,11 +91,18 @@ impl Iterator for QueryState<'_> {
         let mut bindings: BTreeMap<String, Value> = BTreeMap::new();
 
         for (var_key, term_to_be_printed) in &term_write_result.var_dict {
-            if var_key.to_string().starts_with('_') {
-                continue;
+            let var_name = var_key.to_string();
+            if var_name.starts_with('_') {
+                let should_print = var_names.values().any(|x| match x.borrow().clone() {
+                    Var::Named(v) => v == var_name,
+                    _ => false,
+                });
+                if !should_print {
+                    continue;
+                }
             }
 
-            let term = Value::from_heapcell(machine, *term_to_be_printed, var_names);
+            let term = Value::from_heapcell(machine, *term_to_be_printed, &mut var_names.clone());
 
             if let Value::Var(ref term_str) = term {
                 if *term_str == var_key.to_string() {
@@ -682,7 +689,7 @@ mod tests {
                                 ".".into(),
                                 vec![
                                     Value::Atom("b".into()),
-                                    Value::AnonVar, // Anonymous variable
+                                    Value::Var("_A".into()), // Anonymous variable
                                 ],
                             ),
                         ],
@@ -764,4 +771,25 @@ mod tests {
         assert_eq!(iterator.next(), Some(Ok(QueryResolutionLine::False)));
         assert_eq!(iterator.next(), None);
     }
+
+    #[test]
+    #[cfg_attr(miri, ignore)]
+    fn differentiate_anonymous_variables() {
+        let mut machine = Machine::new_lib();
+
+        let result = machine.run_query("A = [_,_], _B = 1 ; B = [_,_].".into());
+
+        assert_eq!(
+            result,
+            Ok(QueryResolution::Matches(vec![
+                QueryMatch::from(btreemap! {
+                    "A" => Value::List(vec![Value::Var("_A".into()), Value::Var("_C".into())]),
+                    "_B" => Value::Integer(1.into()),
+                }),
+                QueryMatch::from(btreemap! {
+                    "B" => Value::List(vec![Value::Var("_A".into()), Value::Var("_C".into())]),
+                }),
+            ]))
+        );
+    }
 }
index 94dd5d98601422d0632a3f27ec50099fe4010f97..06a8873b3d07cd1b4e3f707c4171993021358bfe 100644 (file)
@@ -1,10 +1,11 @@
 use crate::atom_table::*;
 use crate::heap_iter::{stackful_post_order_iter, NonListElider};
 use crate::machine::{F64Offset, F64Ptr, Fixnum, HeapCellValueTag};
-use crate::parser::ast;
+use crate::parser::ast::{Var, VarPtr};
 use dashu::*;
 use indexmap::IndexMap;
 use ordered_float::OrderedFloat;
+use std::cmp::Ordering;
 use std::collections::BTreeMap;
 use std::collections::HashMap;
 use std::fmt::Display;
@@ -138,14 +139,31 @@ pub enum Value {
     List(Vec<Value>),
     Structure(String, Vec<Value>),
     Var(String),
-    AnonVar,
+}
+
+/// This is an auxiliary function to turn a count into names of anonymous variables like _A, _B,
+/// _AB, etc...
+fn count_to_letter_code(mut count: usize) -> String {
+    let mut letters = Vec::new();
+
+    loop {
+        let letter_idx = (count % 26) as u32;
+        letters.push(char::from_u32('A' as u32 + letter_idx).unwrap());
+        count /= 26;
+
+        if count == 0 {
+            break;
+        }
+    }
+
+    letters.into_iter().chain("_".chars()).rev().collect()
 }
 
 impl Value {
     pub(crate) fn from_heapcell(
         machine: &mut Machine,
         heap_cell: HeapCellValue,
-        var_names: &IndexMap<HeapCellValue, ast::VarPtr>,
+        var_names: &mut IndexMap<HeapCellValue, VarPtr>,
     ) -> Self {
         // Adapted from MachineState::read_term_from_heap
         let mut term_stack = vec![];
@@ -155,6 +173,18 @@ impl Value {
             heap_cell,
         );
 
+        let mut anon_count: usize = 0;
+        let var_ptr_cmp = |a, b| match a {
+            Var::Named(name_a) => match b {
+                Var::Named(name_b) => name_a.cmp(&name_b),
+                _ => Ordering::Less,
+            },
+            _ => match b {
+                Var::Named(_) => Ordering::Greater,
+                _ => Ordering::Equal,
+            },
+        };
+
         for addr in iter {
             let addr = unmark_cell_bits!(addr);
 
@@ -195,18 +225,37 @@ impl Value {
                     term_stack.push(list);
                 }
                 (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => {
-                    if let Some(ast::Var::Named(name)) = var_names.get(&addr).map(|x| x.borrow().clone()) {
-                        term_stack.push(Value::Var(name));
-                    } else {
-                        // TODO: These variables aren't actually anonymous, they just aren't in the
-                        // query. Give names to them to differentiate distinct variables.
-                        term_stack.push(Value::AnonVar);
+                    let var = var_names.get(&addr).map(|x| x.borrow().clone());
+                    match var {
+                        Some(Var::Named(name)) => term_stack.push(Value::Var(name)),
+                        _ => {
+                            let anon_name = loop {
+                                // Generate a name for the anonymous variable
+                                let anon_name = count_to_letter_code(anon_count);
+
+                                // Find if this name is already being used
+                                var_names.sort_by(|_, a, _, b| {
+                                    var_ptr_cmp(a.borrow().clone(), b.borrow().clone())
+                                });
+                                let binary_result = var_names.binary_search_by(|_,a| {
+                                    let var_ptr = Var::Named(anon_name.clone());
+                                    var_ptr_cmp(a.borrow().clone(), var_ptr.clone())
+                                });
+
+                                match binary_result {
+                                    Ok(_) => anon_count += 1, // Name already used
+                                    Err(_) => {
+                                        // Name not used, assign it to this variable
+                                        let var_ptr = VarPtr::from(Var::Named(anon_name.clone()));
+                                        var_names.insert(addr, var_ptr);
+                                        break anon_name;
+                                    },
+                                }
+                            };
+                            term_stack.push(Value::Var(anon_name));
+                        },
                     }
                 }
-                //(HeapCellValueTag::Cons | HeapCellValueTag::CStr | HeapCellValueTag::Fixnum |
-                // HeapCellValueTag::Char | HeapCellValueTag::F64) => {
-                //    term_stack.push(Term::Literal(Cell::default(), Literal::try_from(addr).unwrap()));
-                //}
                 (HeapCellValueTag::F64, f) => {
                     term_stack.push(Value::Float(*f));
                 }