]> Repositorios git - scryer-prolog.git/commitdiff
add atom_length/2, atom_chars/2, atom_codes/2
authorMark Thom <[email protected]>
Sat, 6 Apr 2019 05:00:53 +0000 (23:00 -0600)
committerMark Thom <[email protected]>
Sat, 6 Apr 2019 05:00:53 +0000 (23:00 -0600)
README.md
src/prolog/clause_types.rs
src/prolog/lib/builtins.pl
src/prolog/machine/machine_errors.rs
src/prolog/machine/machine_state_impl.rs
src/prolog/machine/system_calls.rs
src/tests.rs

index 13393927f6de9664af7c0c3e826dba6abd4472fe..ee13817521ce48ac66146581e3554547624588f7 100644 (file)
--- a/README.md
+++ b/README.md
@@ -159,6 +159,9 @@ The following predicates are built-in to Scryer.
 * `assertz/1`
 * `atom/1`
 * `atomic/1`
+* `atom_chars/2`
+* `atom_codes/2`
+* `atom_length/2`
 * `bagof/3`
 * `bb_b_put/2`
 * `bb_get/2`
index e60a7a4cebf46a8f5d9cdc7d6416d3731172f6f5..520d2ecb6375a7cb82cc3047040a0dd51c306cb4 100644 (file)
@@ -130,7 +130,7 @@ ref_thread_local! {
         m.insert(("partial_string", 2), ClauseType::BuiltIn(BuiltInClauseType::PartialString));
         m.insert(("read", 1), ClauseType::BuiltIn(BuiltInClauseType::Read));
         m.insert(("sort", 2), ClauseType::BuiltIn(BuiltInClauseType::Sort));
-
+        
         m
     };
 }
@@ -159,6 +159,9 @@ pub enum SystemClauseType {
     AbolishModuleClause,
     AssertDynamicPredicateToBack,
     AssertDynamicPredicateToFront,
+    AtomChars,
+    AtomCodes,
+    AtomLength,
     ModuleAssertDynamicPredicateToFront,
     ModuleAssertDynamicPredicateToBack,
     CheckCutPoint,
@@ -231,9 +234,12 @@ impl SystemClauseType {
     pub fn name(&self) -> ClauseName {
         match self {
             &SystemClauseType::AbolishClause => clause_name!("$abolish_clause"),
-            &SystemClauseType::AbolishModuleClause => clause_name!("$abolish_module_clause"),
+            &SystemClauseType::AbolishModuleClause => clause_name!("$abolish_module_clause"),            
             &SystemClauseType::AssertDynamicPredicateToBack => clause_name!("$assertz"),
             &SystemClauseType::AssertDynamicPredicateToFront => clause_name!("$asserta"),
+            &SystemClauseType::AtomChars => clause_name!("$atom_chars"),
+            &SystemClauseType::AtomCodes => clause_name!("$atom_codes"),
+            &SystemClauseType::AtomLength => clause_name!("$atom_length"),
             &SystemClauseType::ModuleAssertDynamicPredicateToFront => clause_name!("$module_asserta"),
             &SystemClauseType::ModuleAssertDynamicPredicateToBack => clause_name!("$module_assertz"),
             &SystemClauseType::CheckCutPoint => clause_name!("$check_cp"),
@@ -306,6 +312,9 @@ impl SystemClauseType {
     pub fn from(name: &str, arity: usize) -> Option<SystemClauseType> {
         match (name, arity) {
             ("$abolish_clause", 2) => Some(SystemClauseType::AbolishClause),
+            ("$atom_chars", 2)  => Some(SystemClauseType::AtomChars),
+            ("$atom_codes", 2)  => Some(SystemClauseType::AtomCodes),
+            ("$atom_length", 2) => Some(SystemClauseType::AtomLength),
             ("$abolish_module_clause", 3) => Some(SystemClauseType::AbolishModuleClause),
             ("$module_asserta", 5) => Some(SystemClauseType::ModuleAssertDynamicPredicateToFront),
             ("$module_assertz", 5) => Some(SystemClauseType::ModuleAssertDynamicPredicateToBack),
@@ -382,7 +391,7 @@ impl SystemClauseType {
 #[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
 pub enum BuiltInClauseType {
     AcyclicTerm,
-    Arg,
+    Arg, 
     Compare,
     CompareTerm(CompareTermQT),
     CyclicTerm,
index 271680f83439a19772fd9ed7152546f4c9076ba9..c8bece283b2eaecffc54fbaaa3b47f0fbd816c60 100644 (file)
@@ -6,14 +6,15 @@
        (mod)/2, (rem)/2, (>)/2, (<)/2, (=\=)/2, (=:=)/2, (>=)/2,
        (=<)/2, (,)/2, (->)/2, (;)/2, (=..)/2, (==)/2, (\==)/2,
        (@=<)/2, (@>=)/2, (@<)/2, (@>)/2, (=@=)/2, (\=@=)/2, (:)/2,
-       abolish/1, asserta/1, assertz/1, bagof/3, bb_b_put/2,
-       bb_get/2, bb_put/2, call_cleanup/2,
-       call_with_inference_limit/3, catch/3, clause/2,
-       current_predicate/1, current_op/3, current_prolog_flag/2,
-       expand_goal/2, expand_term/2, findall/3, findall/4, halt/0,
-       once/1, op/3, repeat/0, retract/1, set_prolog_flag/2, setof/3,
-       setup_call_cleanup/3, term_variables/2, throw/1, true/0,
-       false/0, write/1, write_canonical/1, writeq/1, write_term/2]).
+       abolish/1, asserta/1, assertz/1, atom_chars/2, atom_codes/2,
+       atom_length/2, bagof/3, bb_b_put/2, bb_get/2, bb_put/2,
+       call_cleanup/2, call_with_inference_limit/3, catch/3,
+       clause/2, current_predicate/1, current_op/3,
+       current_prolog_flag/2, expand_goal/2, expand_term/2,
+       findall/3, findall/4, halt/0, once/1, op/3, repeat/0,
+       retract/1, set_prolog_flag/2, setof/3, setup_call_cleanup/3,
+       term_variables/2, throw/1, true/0, false/0, write/1,
+       write_canonical/1, writeq/1, write_term/2]).
 
 /* this is an implementation specific declarative operator used to implement call_with_inference_limit/3
    and setup_call_cleanup/3. switches to the default trust_me and retry_me_else. Indexing choice
@@ -444,7 +445,7 @@ findall_with_existential(Template, Goal, PairedSolutions, Witnesses0, Witnesses)
     ;  Witnesses = Witnesses0,
        findall(Witnesses-Template, Goal, PairedSolutions)
     ).
-    
+
 bagof(Template, Goal, Solution) :-
     error:can_be(list, Solution),
     term_variables(Template, TemplateVars0),
@@ -751,7 +752,7 @@ op_specifier(OpSpec) :- atom(OpSpec),
     ).
 op_specifier(OpSpec) :- throw(error(type_error(atom, OpSpec), op/3)).
 
-valid_op(Op) :- atom(Op),    
+valid_op(Op) :- atom(Op),
     (  Op == (,) -> throw(error(permission_error(modify, operator, (,)), op/3)) % 8.14.3.3 j), k).
     ;  Op == {} -> throw(error(permission_error(create, operator, {}), op/3))
     ;  Op == [] -> throw(error(permission_error(create, operator, []), op/3))
@@ -794,3 +795,45 @@ bb_get(Key, Value) :- atom(Key), !, '$fetch_global_var'(Key, Value).
 bb_get(Key, _) :- throw(error(type_error(atom, Key), bb_get/2)).
 
 halt :- '$halt'.
+
+atom_length(Atom, Length) :-
+    (  var(Atom)  -> throw(error(instantiation_error, atom_length/2)) % 8.16.1.3 a)
+    ;  atom(Atom) -> (  var(Length) -> '$atom_length'(Atom, Length)
+                    ;  integer(Length), Length >= 0 -> '$atom_length'(Atom, Length)
+                    ;  integer(Length) -> throw(domain_error(not_less_than_zero, Length), atom_length/2) % 8.16.1.3 d)
+                    ;  throw(error(type_error(integer, Length), atom_length/2)) % 8.16.1.3 c)
+                    )
+    ;  throw(error(type_error(atom, Atom), atom_length/2)) % 8.16.1.3 b)
+    ).
+
+no_var_in_list([]).
+no_var_in_list([X|Xs]) :- var(X), !, '$fail'.
+no_var_in_list([_|Xs]) :- no_var_in_list(Xs).
+
+atom_chars(Atom, List) :-
+    (  var(Atom), '$skip_max_list'(_, -1, List, Xs) ->
+       (  var(Xs) -> throw(error(instantiation_error, atom_chars/2))
+       ;  Xs == [] ->
+         (  no_var_in_list(List) -> '$atom_chars'(Atom, List)
+         ;  throw(error(instantiation_error, atom_chars/2))
+         )
+       ;  throw(error(type_error(list, List), atom_chars/2))
+       )
+    ;  atom(Atom) -> '$atom_chars'(Atom, List)
+    ;  Atom == [] -> '$atom_chars'(Atom, List)
+    ;  throw(error(type_error(atom, Atom), atom_chars/2))
+    ).
+
+atom_codes(Atom, List) :-
+    (  var(Atom), '$skip_max_list'(_, -1, List, Xs) ->
+       (  var(Xs) -> throw(error(instantiation_error, atom_codes/2))
+       ;  Xs == [] ->
+         (  no_var_in_list(List) -> '$atom_codes'(Atom, List)
+         ;  throw(error(instantiation_error, atom_codes/2))
+         )
+       ;  throw(error(type_error(list, List), atom_codes/2))
+       )
+    ;  atom(Atom) -> '$atom_codes'(Atom, List)
+    ;  Atom == [] -> '$atom_codes'(Atom, List)
+    ;  throw(error(type_error(atom, Atom), atom_codes/2))
+    ).
index c7afcbbc48cb5216d4bd9f7fb82fa7c582e37c3e..59d670861865f0205d255f46b194498f620affd2 100644 (file)
@@ -172,7 +172,7 @@ pub enum ValidType {
 //    Boolean,
 //    Byte,
     Callable,
-//    Character,
+    Character,
     Compound,
 //    Evaluable,
 //    InByte,
@@ -193,7 +193,7 @@ impl ValidType {
 //            ValidType::Boolean => "boolean",
 //            ValidType::Byte => "byte",
             ValidType::Callable => "callable",
-//            ValidType::Character => "character",
+            ValidType::Character => "character",
             ValidType::Compound => "compound",
 //            ValidType::Evaluable => "evaluable",
 //            ValidType::InByte => "in_byte",
@@ -225,7 +225,7 @@ impl DomainError {
 #[derive(Clone, Copy)]
 pub enum RepFlag {
 //    Character,
-//    CharacterCode,
+    CharacterCode,
 //    InCharacterCode,
     MaxArity,
 //    MaxInteger,
@@ -236,7 +236,7 @@ impl RepFlag {
     pub fn as_str(self) -> &'static str {
         match self {
 //            RepFlag::Character => "character",
-//            RepFlag::CharacterCode => "character_code",
+            RepFlag::CharacterCode => "character_code",
 //            RepFlag::InCharacterCode => "in_character_code",
             RepFlag::MaxArity => "max_arity",
 //            RepFlag::MaxInteger => "max_integer",
index 64212b95b40a909add2f1e06123e260b593d79d3..5c79e0d050a8f9b7015af5516eabd5a208e957d7 100644 (file)
@@ -2107,8 +2107,8 @@ impl MachineState {
         *list = result;
     }
 
-    pub(super) fn try_from_list(&self, r: RegType, caller: MachineStub)
-                                -> Result<Vec<Addr>, MachineStub>
+    pub(super)
+    fn try_from_list(&self, r: RegType, caller: MachineStub) -> Result<Vec<Addr>, MachineStub>
     {
         let a1 = self.store(self.deref(self[r].clone()));
 
index 9555c2b2f95622e1c85426eb90480966a8384985..f23bd7305d35296c4158336aa1251f6d40a8bc19 100644 (file)
@@ -16,6 +16,7 @@ use ref_thread_local::RefThreadLocal;
 
 use std::collections::HashSet;
 use std::io::{stdout, Write};
+use std::iter::once;
 use std::mem;
 use std::rc::Rc;
 
@@ -332,6 +333,133 @@ impl MachineState {
                 self.p = CodePtr::DynamicTransaction(trans_type, p);
                 return Ok(());
             },
+            &SystemClauseType::AtomChars => {
+                let a1 = self[temp_v!(1)].clone();
+
+                match self.store(self.deref(a1)) {
+                    Addr::Con(Constant::Char(c)) => {
+                        let iter = once(Addr::Con(Constant::Char(c)));
+                        let list_of_chars = Addr::HeapCell(self.heap.to_list(iter));
+
+                        let a2 = self[temp_v!(2)].clone();
+                        self.unify(a2, list_of_chars);
+                    },
+                    Addr::Con(Constant::Atom(name, _)) => {
+                        let iter = name.as_str().chars().map(|c| Addr::Con(Constant::Char(c)));
+                        let list_of_chars = Addr::HeapCell(self.heap.to_list(iter));
+
+                        let a2 = self[temp_v!(2)].clone();
+                        self.unify(a2, list_of_chars);
+                    },
+                    Addr::Con(Constant::EmptyList) => {
+                        let a2 = self[temp_v!(2)].clone();
+                        let chars = vec![Addr::Con(Constant::Char('[')),
+                                         Addr::Con(Constant::Char(']'))];
+                        
+                        let list_of_chars = Addr::HeapCell(self.heap.to_list(chars.into_iter()));
+
+                        self.unify(a2, list_of_chars);
+                    },
+                    ref addr if addr.is_ref() => {
+                        let stub = MachineError::functor_stub(clause_name!("atom_chars"), 2);
+
+                        match self.try_from_list(temp_v!(2), stub.clone()) {
+                            Err(e) => return Err(e),
+                            Ok(addrs) => {
+                                let mut chars = String::new();
+
+                                for addr in addrs.iter() {
+                                    match addr {
+                                        &Addr::Con(Constant::Char(c)) =>
+                                            chars.push(c),
+                                        &Addr::Con(Constant::Atom(ref name, _))
+                                            if name.as_str().len() == 1 => {
+                                                chars += name.as_str();
+                                            },
+                                        _ => {
+                                            let err = MachineError::type_error(ValidType::Character,
+                                                                               addr.clone());
+                                            return Err(self.error_form(err, stub));
+                                        }
+                                    }
+                                }
+
+                                let chars = clause_name!(chars, indices.atom_tbl);
+                                self.unify(addr.clone(), Addr::Con(Constant::Atom(chars, None)));
+                            }
+                        }
+                    },
+                    _ => unreachable!()
+                };
+            },
+            &SystemClauseType::AtomCodes => {
+                let a1 = self[temp_v!(1)].clone();
+
+                match self.store(self.deref(a1)) {
+                    Addr::Con(Constant::Char(c)) => {
+                        let iter = once(Addr::Con(Constant::CharCode(c as u8)));
+                        let list_of_codes = Addr::HeapCell(self.heap.to_list(iter));
+
+                        let a2 = self[temp_v!(2)].clone();
+                        self.unify(a2, list_of_codes);
+                    },                    
+                    Addr::Con(Constant::Atom(name, _)) => {
+                        let iter = name.as_str().chars().map(|c| Addr::Con(Constant::CharCode(c as u8)));
+                        let list_of_codes = Addr::HeapCell(self.heap.to_list(iter));
+
+                        let a2 = self[temp_v!(2)].clone();
+
+                        self.unify(a2, list_of_codes);
+                    },
+                    Addr::Con(Constant::EmptyList) => {
+                        let a2 = self[temp_v!(2)].clone();
+                        let chars = vec![Addr::Con(Constant::CharCode('[' as u8)),
+                                         Addr::Con(Constant::CharCode(']' as u8))];
+                        
+                        let list_of_codes = Addr::HeapCell(self.heap.to_list(chars.into_iter()));
+
+                        self.unify(a2, list_of_codes);
+                    },
+                    ref addr if addr.is_ref() => {
+                        let stub = MachineError::functor_stub(clause_name!("atom_codes"), 2);
+
+                        match self.try_from_list(temp_v!(2), stub.clone()) {
+                            Err(e) => return Err(e),
+                            Ok(addrs) => {
+                                let mut chars = String::new();
+
+                                for addr in addrs.iter() {
+                                    match addr {
+                                        &Addr::Con(Constant::CharCode(c)) =>
+                                            chars.push(c as char),
+                                        _ => {
+                                            let err = MachineError::representation_error(RepFlag::CharacterCode);
+                                            return Err(self.error_form(err, stub));
+                                        }
+                                    }
+                                }
+
+                                let chars = clause_name!(chars, indices.atom_tbl);
+                                self.unify(addr.clone(), Addr::Con(Constant::Atom(chars, None)));
+                            }
+                        }
+                    },
+                    _ => unreachable!()
+                };
+            },
+            &SystemClauseType::AtomLength => {
+                let a1 = self[temp_v!(1)].clone();
+
+                let atom = match self.store(self.deref(a1)) {
+                    Addr::Con(Constant::Atom(name, _)) => name,
+                    _ => unreachable!()
+                };
+
+                let len = Number::Integer(Rc::new(BigInt::from_usize(atom.as_str().len()).unwrap()));
+                let a2  = self[temp_v!(2)].clone();
+                
+                self.unify(a2, Addr::Con(Constant::Number(len)));
+            },
             &SystemClauseType::ModuleAssertDynamicPredicateToFront => {
                 let p = self.cp;
                 let trans_type = DynamicTransactionType::ModuleAssert(DynamicAssertPlace::Front);
@@ -583,13 +711,13 @@ impl MachineState {
 
                       unossified_op_dir.extend(indices.op_dir.iter().filter_map(|(key, op_dir_val)| {
                           let (name, fixity) = key.clone();
-                          
+
                           let prec  = op_dir_val.shared_op_desc().prec();
 
                           if prec == 0 {
                               return None;
                           }
-                          
+
                           let assoc = op_dir_val.shared_op_desc().assoc();
 
                           Some((OrderedOpDirKey(name, fixity), (prec, assoc)))
@@ -651,8 +779,8 @@ impl MachineState {
                     Addr::DBRef(db_ref) =>
                         match db_ref {
                             DBRef::Op(priority, spec, name, _, shared_op_desc) => {
-                                
-                                
+
+
                                 let prec = self[temp_v!(2)].clone();
                                 let specifier = self[temp_v!(3)].clone();
                                 let op = self[temp_v!(4)].clone();
index 56287c347226528135c325a1e16cbed3ef1630b5..f28ef5361be6664b06e693f61f67445669e0f94a 100644 (file)
@@ -1952,6 +1952,19 @@ foo(X) :- call(X) -> call(X).");
     assert_prolog_success!(&mut wam, "?- catch(abolish(foo(_)), error(type_error(predicate_indicator, foo(_)), abolish/1), true).");
     assert_prolog_failure!(&mut wam, "?- abolish(abolish/1).");
     assert_prolog_success!(&mut wam, "?- catch(abolish(abolish/1), error(permission_error(modify, static_procedure, abolish/1), abolish/1), true).");
+
+    assert_prolog_success!(&mut wam, "?- atom_length('enchanted evening', N).",
+                           [["N = 17"]]);
+    assert_prolog_success!(&mut wam, r"?- atom_length('enchanted\
+ evening', N).",
+                           [["N = 17"]]);
+    assert_prolog_success!(&mut wam, "?- atom_length('', N).",
+                           [["N = 0"]]);
+    assert_prolog_failure!(&mut wam, "?- atom_length('scarlet', 5).");
+    assert_prolog_success!(&mut wam, "?- catch((atom_length(Atom, 4), false), error(instantiation_error, _), true).");
+    assert_prolog_success!(&mut wam, "?- catch((atom_length(1.23, 4), false), error(type_error(atom, 1.23), _), true).");
+    assert_prolog_success!(&mut wam, "?- catch((atom_length(atom, '4'), false), error(type_error(integer, '4'), _), true).");
+
 }