finalize support for partial strings

This commit is contained in:
Mark Thom
2018-09-02 23:32:51 -06:00
parent 2818e851a0
commit abcb53699a
14 changed files with 311 additions and 104 deletions

View File

@@ -33,7 +33,7 @@ Extend rusty-wam to include the following, among other features:
* Default representation of strings as list of chars, using a packed * Default representation of strings as list of chars, using a packed
internal representation (_done_). internal representation (_done_).
- A representation of 'partial strings' as difference lists - A representation of 'partial strings' as difference lists
of characters (_in progress_). of characters (_done_).
* `term_expansion/2` and `goal_expansion/2` (_in progress_). * `term_expansion/2` and `goal_expansion/2` (_in progress_).
* Definite Clause Grammars. * Definite Clause Grammars.
* Attributed variables using the SICStus Prolog interface and * Attributed variables using the SICStus Prolog interface and
@@ -148,6 +148,7 @@ The following predicates are built-in to rusty-wam.
* `ground/1` * `ground/1`
* `integer/1` * `integer/1`
* `is_list/1` * `is_list/1`
* `is_partial_string/1`
* `keysort/2` * `keysort/2`
* `length/2` * `length/2`
* `maplist/2..9` * `maplist/2..9`
@@ -155,6 +156,7 @@ The following predicates are built-in to rusty-wam.
* `memberchk/2` * `memberchk/2`
* `nonvar/1` * `nonvar/1`
* `once/1` * `once/1`
* `partial_string/2`
* `rational/1` * `rational/1`
* `read/1` * `read/1`
* `repeat/0` * `repeat/0`
@@ -252,6 +254,25 @@ true.
New operators can be defined using the `op` declaration. New operators can be defined using the `op` declaration.
### Partial strings
rusty-wam has two specialized, non-ISO builtin predicates for handling
so-called "partial strings". Partial strings imitate difference lists
of characters, but are much more space efficient. This efficiency
comes at the cost of full generality -- you cannot unify the tail
variables of two distinct partial strings, because their buffers will
always be distinct.
If `X` is a free variable, the query
`?- partial_string("abc", X), X = [a, b, c | Y], is_partial_string(X),
is_partial_string(Y).`
will succeed. Further, if `Y` a free variable, unifying `Y` against
another string, "def" in this case, produces the equations
`X = [a, b, c, d, e, f], Y = [d, e, f].`
### Modules ### Modules
rusty-wam has a simple predicate-based module system. It provides a rusty-wam has a simple predicate-based module system. It provides a

View File

@@ -545,11 +545,7 @@ impl PartialEq for Constant {
(&Constant::Atom(ref atom), &Constant::Char(c)) (&Constant::Atom(ref atom), &Constant::Char(c))
| (&Constant::Char(c), &Constant::Atom(ref atom)) => { | (&Constant::Char(c), &Constant::Atom(ref atom)) => {
let s = atom.as_str(); let s = atom.as_str();
if c.len_utf8() != s.len() || Some(c) != s.chars().next() { c.len_utf8() == s.len() && Some(c) == s.chars().next()
false
} else {
true
}
}, },
(&Constant::Atom(ref a1), &Constant::Atom(ref a2)) => (&Constant::Atom(ref a1), &Constant::Atom(ref a2)) =>
a1.as_str() == a2.as_str(), a1.as_str() == a2.as_str(),
@@ -606,7 +602,8 @@ pub enum InlinedClauseType {
IsString(RegType), IsString(RegType),
IsFloat(RegType), IsFloat(RegType),
IsNonVar(RegType), IsNonVar(RegType),
IsVar(RegType), IsPartialString(RegType),
IsVar(RegType)
} }
impl InlinedClauseType { impl InlinedClauseType {
@@ -621,7 +618,8 @@ impl InlinedClauseType {
&InlinedClauseType::IsString(..) => "string", &InlinedClauseType::IsString(..) => "string",
&InlinedClauseType::IsFloat (..) => "float", &InlinedClauseType::IsFloat (..) => "float",
&InlinedClauseType::IsNonVar(..) => "nonvar", &InlinedClauseType::IsNonVar(..) => "nonvar",
&InlinedClauseType::IsVar(..) => "var" &InlinedClauseType::IsPartialString(..) => "partial_string",
&InlinedClauseType::IsVar(..) => "var",
} }
} }
@@ -654,6 +652,7 @@ impl InlinedClauseType {
("float", 1) => Some(InlinedClauseType::IsFloat(r1)), ("float", 1) => Some(InlinedClauseType::IsFloat(r1)),
("nonvar", 1) => Some(InlinedClauseType::IsNonVar(r1)), ("nonvar", 1) => Some(InlinedClauseType::IsNonVar(r1)),
("var", 1) => Some(InlinedClauseType::IsVar(r1)), ("var", 1) => Some(InlinedClauseType::IsVar(r1)),
("is_partial_string", 1) => Some(InlinedClauseType::IsPartialString(r1)),
_ => None _ => None
} }
} }
@@ -858,6 +857,7 @@ pub enum BuiltInClauseType {
Is(RegType, ArithmeticTerm), Is(RegType, ArithmeticTerm),
KeySort, KeySort,
NotEq, NotEq,
PartialString,
Read, Read,
Sort, Sort,
} }
@@ -963,6 +963,7 @@ impl BuiltInClauseType {
&BuiltInClauseType::NotEq => clause_name!("\\=="), &BuiltInClauseType::NotEq => clause_name!("\\=="),
&BuiltInClauseType::Read => clause_name!("read"), &BuiltInClauseType::Read => clause_name!("read"),
&BuiltInClauseType::Sort => clause_name!("sort"), &BuiltInClauseType::Sort => clause_name!("sort"),
&BuiltInClauseType::PartialString => clause_name!("partial_string")
} }
} }
@@ -983,6 +984,7 @@ impl BuiltInClauseType {
&BuiltInClauseType::NotEq => 2, &BuiltInClauseType::NotEq => 2,
&BuiltInClauseType::Read => 1, &BuiltInClauseType::Read => 1,
&BuiltInClauseType::Sort => 2, &BuiltInClauseType::Sort => 2,
&BuiltInClauseType::PartialString => 1,
} }
} }

View File

@@ -71,7 +71,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker>
*self.var_count.get(var).unwrap() *self.var_count.get(var).unwrap()
} }
fn mark_non_callable(&mut self, name: Rc<Atom>, arity: usize, term_loc: GenContext, fn mark_non_callable(&mut self, name: Rc<Var>, arity: usize, term_loc: GenContext,
vr: &'a Cell<VarReg>, code: &mut Code) vr: &'a Cell<VarReg>, code: &mut Code)
-> RegType -> RegType
{ {
@@ -133,8 +133,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker>
}; };
} }
fn compile_target<Target, Iter>(&mut self, iter: Iter, term_loc: GenContext, is_exposed: bool) fn compile_target<Target, Iter>(&mut self, iter: Iter, term_loc: GenContext, is_exposed: bool) -> Vec<Target>
-> Vec<Target>
where Target: CompilationTarget<'a>, Iter: Iterator<Item=TermRef<'a>> where Target: CompilationTarget<'a>, Iter: Iterator<Item=TermRef<'a>>
{ {
let mut target = Vec::new(); let mut target = Vec::new();
@@ -385,6 +384,14 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker>
let r = self.mark_non_callable(name.clone(), 1, term_loc, vr, code); let r = self.mark_non_callable(name.clone(), 1, term_loc, vr, code);
code.push(is_var!(r)); code.push(is_var!(r));
} }
},
&InlinedClauseType::IsPartialString(..) =>
match terms[0].as_ref() {
&Term::Var(ref vr, ref name) => {
let r = self.mark_non_callable(name.clone(), 1, term_loc, vr, code);
code.push(is_partial_string!(r));
},
_ => code.push(fail!())
} }
} }
@@ -401,10 +408,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker>
code: &mut Code, is_exposed: bool) code: &mut Code, is_exposed: bool)
-> Result<(), ParserError> -> Result<(), ParserError>
{ {
for (chunk_num, _, terms) in iter.rule_body_iter() for (chunk_num, _, terms) in iter.rule_body_iter() {
{ for (i, term) in terms.iter().enumerate() {
for (i, term) in terms.iter().enumerate()
{
let term_loc = if i + 1 < terms.len() { let term_loc = if i + 1 < terms.len() {
GenContext::Mid(chunk_num) GenContext::Mid(chunk_num)
} else { } else {

View File

@@ -136,8 +136,7 @@ impl MachineState {
HCPostOrderIterator::new(HCPreOrderIterator::new(self, a)) HCPostOrderIterator::new(HCPreOrderIterator::new(self, a))
} }
pub fn acyclic_pre_order_iter<'a>(&'a self, a: Addr) pub fn acyclic_pre_order_iter<'a>(&'a self, a: Addr) -> HCAcyclicIterator<HCPreOrderIterator<'a>>
-> HCAcyclicIterator<HCPreOrderIterator<'a>>
{ {
HCAcyclicIterator::new(HCPreOrderIterator::new(self, a)) HCAcyclicIterator::new(HCPreOrderIterator::new(self, a))
} }
@@ -182,6 +181,7 @@ where HCIter: Iterator<Item=HeapCellValue> + MutStackHCIterator
if !self.seen.contains(&addr) { if !self.seen.contains(&addr) {
self.iter.stack().push(addr.clone()); self.iter.stack().push(addr.clone());
self.seen.insert(addr); self.seen.insert(addr);
break; break;
} }
} }

View File

@@ -335,10 +335,13 @@ impl<'a, Formatter: HCValueFormatter, Outputter: HCValueOutputter>
if self.machine_st.machine_flags().double_quotes.is_chars() { if self.machine_st.machine_flags().double_quotes.is_chars() {
if !s.is_empty() { if !s.is_empty() {
self.push_list(); self.push_list();
} else if s.is_expandable() {
if !self.at_cdr(" | _") {
self.outputter.push_char('_');
}
} else if !self.at_cdr("") { } else if !self.at_cdr("") {
self.outputter.append("[]"); self.outputter.append("[]");
} }
// self.expand_char_list(s);
} else { // for now, == DoubleQuotes::Atom } else { // for now, == DoubleQuotes::Atom
let borrowed_str = s.borrow(); let borrowed_str = s.borrow();

View File

@@ -51,8 +51,6 @@ impl<'a> QueryIterator<'a> {
let state = TermIterState::Clause(Level::Root, 0, cell, ct.clone(), terms); let state = TermIterState::Clause(Level::Root, 0, cell, ct.clone(), terms);
QueryIterator { state_stack: vec![state] } QueryIterator { state_stack: vec![state] }
}, },
&QueryTerm::BlockedCut =>
QueryIterator { state_stack: vec![] },
&QueryTerm::UnblockedCut(ref cell) => { &QueryTerm::UnblockedCut(ref cell) => {
let state = TermIterState::Var(Level::Root, cell, rc_atom!("!")); let state = TermIterState::Var(Level::Root, cell, rc_atom!("!"));
QueryIterator { state_stack: vec![state] } QueryIterator { state_stack: vec![state] }
@@ -67,7 +65,9 @@ impl<'a> QueryIterator<'a> {
}).collect(); }).collect();
QueryIterator { state_stack } QueryIterator { state_stack }
} },
&QueryTerm::BlockedCut =>
QueryIterator { state_stack: vec![] },
} }
} }
} }
@@ -353,8 +353,7 @@ impl<'a> ChunkedIterator<'a>
result.push(term), result.push(term),
ChunkedTerm::BodyTerm(&QueryTerm::Clause(_, ClauseType::Inlined(_), ..)) => ChunkedTerm::BodyTerm(&QueryTerm::Clause(_, ClauseType::Inlined(_), ..)) =>
result.push(term), result.push(term),
ChunkedTerm::BodyTerm(&QueryTerm::Clause(_, ClauseType::CallN, ref subterms, _)) => ChunkedTerm::BodyTerm(&QueryTerm::Clause(_, ClauseType::CallN, ref subterms, _)) => {
{
result.push(term); result.push(term);
arity = subterms.len() + 1; arity = subterms.len() + 1;
break; break;

View File

@@ -657,6 +657,19 @@ pub(crate) trait CallPolicy: Any {
return_from_clause!(machine_st.last_call, machine_st) return_from_clause!(machine_st.last_call, machine_st)
}, },
&BuiltInClauseType::PartialString => {
let a1 = machine_st[temp_v!(1)].clone();
let a2 = machine_st[temp_v!(2)].clone();
if let Addr::Con(Constant::String(s)) = a1 {
s.set_expandable();
machine_st.write_constant_to_var(a2, Constant::String(s));
} else {
machine_st.fail = true;
}
return_from_clause!(machine_st.last_call, machine_st)
},
&BuiltInClauseType::Sort => { &BuiltInClauseType::Sort => {
machine_st.check_sort_errors()?; machine_st.check_sort_errors()?;

View File

@@ -9,6 +9,7 @@ use prolog::num::{Integer, Signed, ToPrimitive, Zero};
use prolog::num::bigint::{BigInt, BigUint}; use prolog::num::bigint::{BigInt, BigUint};
use prolog::num::rational::Ratio; use prolog::num::rational::Ratio;
use prolog::or_stack::*; use prolog::or_stack::*;
use prolog::string_list::StringList;
use std::cell::RefCell; use std::cell::RefCell;
use std::cmp::{max, Ordering}; use std::cmp::{max, Ordering};
@@ -190,21 +191,51 @@ impl MachineState {
self.fail = true; self.fail = true;
}, },
(Addr::Lis(a1), Addr::Con(Constant::String(ref s))) (Addr::Lis(a1), Addr::Con(Constant::String(ref mut s)))
| (Addr::Con(Constant::String(ref s)), Addr::Lis(a1)) | (Addr::Con(Constant::String(ref mut s)), Addr::Lis(a1))
if self.flags.double_quotes.is_chars() => if self.flags.double_quotes.is_chars() => {
if let Some(c) = s.head() { if let Some(c) = s.head() {
pdl.push(Addr::Con(Constant::String(s.tail()))); pdl.push(Addr::Con(Constant::String(s.tail())));
pdl.push(Addr::HeapCell(a1 + 1)); pdl.push(Addr::HeapCell(a1 + 1));
pdl.push(Addr::Con(Constant::Char(c))); pdl.push(Addr::Con(Constant::Char(c)));
pdl.push(Addr::HeapCell(a1)); pdl.push(Addr::HeapCell(a1));
} else {
continue;
} else if s.is_expandable() {
let mut stepper = |c| {
let new_s = s.push_char(c);
pdl.push(Addr::HeapCell(a1 + 1));
pdl.push(Addr::Con(Constant::String(new_s)));
};
match self.heap[a1].clone() {
HeapCellValue::Addr(Addr::Con(Constant::Char(c))) => {
stepper(c);
continue;
},
HeapCellValue::Addr(Addr::Con(Constant::Atom(ref a))) =>
if let Some(c) = a.as_str().chars().next() {
if c.len_utf8() == a.as_str().len() {
stepper(c);
continue;
}
},
_ => {}
};
}
self.fail = true; self.fail = true;
}, },
(Addr::Con(Constant::EmptyList), Addr::Con(Constant::String(ref s))) (Addr::Con(Constant::EmptyList), Addr::Con(Constant::String(ref s)))
| (Addr::Con(Constant::String(ref s)), Addr::Con(Constant::EmptyList)) | (Addr::Con(Constant::String(ref s)), Addr::Con(Constant::EmptyList))
if self.flags.double_quotes.is_chars() => { if self.flags.double_quotes.is_chars() => {
if s.is_expandable() && s.is_empty() {
s.set_non_expandable();
continue;
}
self.fail = !s.is_empty(); self.fail = !s.is_empty();
}, },
(Addr::Lis(a1), Addr::Lis(a2)) => { (Addr::Lis(a1), Addr::Lis(a2)) => {
@@ -214,23 +245,42 @@ impl MachineState {
pdl.push(Addr::HeapCell(a1 + 1)); pdl.push(Addr::HeapCell(a1 + 1));
pdl.push(Addr::HeapCell(a2 + 1)); pdl.push(Addr::HeapCell(a2 + 1));
}, },
(Addr::Con(Constant::String(ref s1)), Addr::Con(Constant::String(ref s2))) => { (Addr::Con(Constant::String(ref mut s1)), Addr::Con(Constant::String(ref mut s2))) => {
let mut stepper = |s1: &mut StringList, s2: &mut StringList| -> bool {
if let Some(c1) = s1.head() { if let Some(c1) = s1.head() {
if let Some(c2) = s2.head() { if let Some(c2) = s2.head() {
if c1 == c2 { if c1 == c2 {
pdl.push(Addr::Con(Constant::String(s1.tail()))); pdl.push(Addr::Con(Constant::String(s1.tail())));
pdl.push(Addr::Con(Constant::String(s2.tail()))); pdl.push(Addr::Con(Constant::String(s2.tail())));
continue; return true;
} }
} else if s2.is_expandable() {
pdl.push(Addr::Con(Constant::String(s2.push_char(c1))));
pdl.push(Addr::Con(Constant::String(s1.tail())));
return true;
} }
} else { } else if s1.is_expandable() {
if s2.head().is_none() { if let Some(c) = s2.head() {
continue; pdl.push(Addr::Con(Constant::String(s1.push_char(c))));
} pdl.push(Addr::Con(Constant::String(s2.tail())));
} else if !s2.is_expandable() {
s1.set_non_expandable();
}/*else {
//TODO: unify the tails of s1 and s2? I guess?
}*/
return true;
} else if s2.head().is_none() {
s2.set_non_expandable();
return true;
} }
self.fail = true; false
};
self.fail = !(stepper(s1, s2) || stepper(s2, s1));
}, },
(Addr::Con(ref c1), Addr::Con(ref c2)) => (Addr::Con(ref c1), Addr::Con(ref c2)) =>
if c1 != c2 { if c1 != c2 {
@@ -345,9 +395,7 @@ impl MachineState {
} }
pub(super) fn write_constant_to_var(&mut self, addr: Addr, c: Constant) { pub(super) fn write_constant_to_var(&mut self, addr: Addr, c: Constant) {
let addr = self.deref(addr); match self.store(self.deref(addr)) {
match self.store(addr) {
Addr::HeapCell(hc) => { Addr::HeapCell(hc) => {
self.heap[hc] = HeapCellValue::Addr(Addr::Con(c.clone())); self.heap[hc] = HeapCellValue::Addr(Addr::Con(c.clone()));
self.trail(Ref::HeapCell(hc)); self.trail(Ref::HeapCell(hc));
@@ -356,12 +404,15 @@ impl MachineState {
self.and_stack[fr][sc] = Addr::Con(c.clone()); self.and_stack[fr][sc] = Addr::Con(c.clone());
self.trail(Ref::StackCell(fr, sc)); self.trail(Ref::StackCell(fr, sc));
}, },
Addr::Con(Constant::String(s)) => Addr::Con(Constant::String(ref mut s)) =>
self.fail = match c { self.fail = match c {
Constant::EmptyList Constant::EmptyList if self.flags.double_quotes.is_chars() =>
if self.flags.double_quotes.is_chars() =>
!s.is_empty(), !s.is_empty(),
Constant::String(s2) => s != s2, Constant::String(ref s2) if s.is_empty() && s.is_expandable() => {
s.append(s2);
false
},
Constant::String(s2) => *s != s2,
_ => true _ => true
}, },
Addr::Con(c1) => { Addr::Con(c1) => {
@@ -826,12 +877,17 @@ impl MachineState {
match addr { match addr {
Addr::Con(Constant::String(ref s)) Addr::Con(Constant::String(ref s))
if self.flags.double_quotes.is_chars() => { if self.flags.double_quotes.is_chars() => {
if let Some(c) = s.head() {
let h = self.heap.h; let h = self.heap.h;
if let Some(c) = s.head() {
self.heap.push(HeapCellValue::Addr(Addr::Con(Constant::Char(c)))); self.heap.push(HeapCellValue::Addr(Addr::Con(Constant::Char(c))));
self.heap.push(HeapCellValue::Addr(Addr::Con(Constant::String(s.tail())))); self.heap.push(HeapCellValue::Addr(Addr::Con(Constant::String(s.tail()))));
self.s = h;
self.mode = MachineMode::Read;
} else if s.is_expandable() {
self.heap.push(HeapCellValue::Addr(Addr::Con(Constant::String(s.clone()))));
self.s = h; self.s = h;
self.mode = MachineMode::Read; self.mode = MachineMode::Read;
} else { } else {
@@ -1588,6 +1644,14 @@ impl MachineState {
_ => self.fail = true _ => self.fail = true
}; };
}, },
&InlinedClauseType::IsPartialString(r1) => {
let d = self.store(self.deref(self[r1].clone()));
match d {
Addr::Con(Constant::String(ref s)) if s.is_expandable() => self.p += 1,
_ => self.fail = true
};
}
} }
} }

View File

@@ -143,6 +143,12 @@ macro_rules! is_var {
) )
} }
macro_rules! is_partial_string {
($r:expr) => (
call_clause!(ClauseType::Inlined(InlinedClauseType::IsPartialString($r)), 1, 0)
)
}
macro_rules! call_clause { macro_rules! call_clause {
($ct:expr, $arity:expr, $pvs:expr) => ( ($ct:expr, $arity:expr, $pvs:expr) => (
Line::Control(ControlInstruction::CallClause($ct, $arity, $pvs, false, false)) Line::Control(ControlInstruction::CallClause($ct, $arity, $pvs, false, false))

View File

@@ -37,13 +37,19 @@ impl PartialOrd for StringList {
impl Ord for StringList { impl Ord for StringList {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
self.body.cmp(&other.body) if self.expandable.get() && !self.expandable.get() {
Ordering::Greater
} else if !self.expandable.get() && self.expandable.get() {
Ordering::Less
} else {
self.borrow()[self.cursor ..].cmp(&other.borrow()[other.cursor ..])
}
} }
} }
impl PartialEq for StringList { impl PartialEq for StringList {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.body == other.body && self.cursor == other.cursor && self.expandable == other.expandable self.borrow()[self.cursor ..] == other.borrow()[other.cursor ..] && self.expandable == other.expandable
} }
} }
@@ -61,6 +67,41 @@ impl StringList {
} }
} }
#[inline]
pub fn is_expandable(&self) -> bool {
self.expandable.get()
}
#[inline]
pub fn set_expandable(&self) {
self.expandable.set(true);
}
#[inline]
pub fn set_non_expandable(&self) {
self.expandable.set(false);
}
#[inline]
pub fn push_char(&mut self, c: char) -> Self {
if self.expandable.get() {
self.body.0.borrow_mut().push(c);
let mut new_string_list = self.clone();
new_string_list.cursor += c.len_utf8();
new_string_list
} else {
self.clone()
}
}
#[inline]
pub fn append(&mut self, s: &StringList) {
self.body.0.borrow_mut().extend(s.borrow()[s.cursor ..].chars());
self.expandable.set(s.expandable.get());
}
#[inline] #[inline]
pub fn cursor(&self) -> usize { pub fn cursor(&self) -> usize {
self.cursor self.cursor

View File

@@ -408,8 +408,7 @@ impl RelationWorker {
self.fabricate_rule(fold_by_str(prec_seq, body_term, comma_sym)) self.fabricate_rule(fold_by_str(prec_seq, body_term, comma_sym))
} }
fn to_query_term(&mut self, indices: &mut MachineCodeIndices, term: Term) fn to_query_term(&mut self, indices: &mut MachineCodeIndices, term: Term) -> Result<QueryTerm, ParserError>
-> Result<QueryTerm, ParserError>
{ {
match term { match term {
Term::Constant(r, Constant::Atom(name)) => Term::Constant(r, Constant::Atom(name)) =>
@@ -422,18 +421,21 @@ impl RelationWorker {
Term::Var(_, ref v) if v.as_str() == "!" => Term::Var(_, ref v) if v.as_str() == "!" =>
Ok(QueryTerm::UnblockedCut(Cell::default())), Ok(QueryTerm::UnblockedCut(Cell::default())),
Term::Clause(r, name, mut terms, fixity) => Term::Clause(r, name, mut terms, fixity) =>
if name.as_str() == ";" && terms.len() == 2 { match (name.as_str(), terms.len()) {
(";", 2) => {
let term = Term::Clause(r, name.clone(), terms, fixity); let term = Term::Clause(r, name.clone(), terms, fixity);
let (stub, clauses) = self.fabricate_disjunct(term); let (stub, clauses) = self.fabricate_disjunct(term);
self.queue.push_back(clauses); self.queue.push_back(clauses);
Ok(QueryTerm::Jump(stub)) Ok(QueryTerm::Jump(stub))
} else if name.as_str() == ":" && terms.len() == 2 { },
(":", 2) => {
let callee = *terms.pop().unwrap(); let callee = *terms.pop().unwrap();
let mod_name = *terms.pop().unwrap(); let mod_name = *terms.pop().unwrap();
module_resolution_call(mod_name, callee) module_resolution_call(mod_name, callee)
} else if name.as_str() == "->" && terms.len() == 2 { },
("->", 2) => {
let conq = *terms.pop().unwrap(); let conq = *terms.pop().unwrap();
let prec = *terms.pop().unwrap(); let prec = *terms.pop().unwrap();
@@ -441,21 +443,31 @@ impl RelationWorker {
self.queue.push_back(clauses); self.queue.push_back(clauses);
Ok(QueryTerm::Jump(stub)) Ok(QueryTerm::Jump(stub))
} else if name.as_str() == "$get_level" && terms.len() == 1 { },
("$get_level", 1) =>
if let Term::Var(_, ref var) = *terms[0] { if let Term::Var(_, ref var) = *terms[0] {
Ok(QueryTerm::GetLevelAndUnify(Cell::default(), var.clone())) Ok(QueryTerm::GetLevelAndUnify(Cell::default(), var.clone()))
} else { } else {
Err(ParserError::InadmissibleQueryTerm) Err(ParserError::InadmissibleQueryTerm)
},
("partial_string", 2) => {
if let Term::Constant(_, Constant::String(_)) = *terms[0].clone() {
if let Term::Var(..) = *terms[1].clone() {
let ct = ClauseType::BuiltIn(BuiltInClauseType::PartialString);
return Ok(QueryTerm::Clause(Cell::default(), ct, terms, false));
} }
} else { }
Err(ParserError::InadmissibleQueryTerm)
},
_ => {
let ct = indices.lookup(name, terms.len(), fixity); let ct = indices.lookup(name, terms.len(), fixity);
Ok(QueryTerm::Clause(Cell::default(), ct, terms, false)) Ok(QueryTerm::Clause(Cell::default(), ct, terms, false))
}
}, },
Term::Var(..) => Term::Var(..) =>
Ok(QueryTerm::Clause(Cell::default(), ClauseType::CallN, vec![Box::new(term)], Ok(QueryTerm::Clause(Cell::default(), ClauseType::CallN, vec![Box::new(term)], false)),
false)), _ => Err(ParserError::InadmissibleQueryTerm)
_ =>
Err(ParserError::InadmissibleQueryTerm)
} }
} }

View File

@@ -1826,7 +1826,8 @@ fn test_queries_on_string_lists()
[["X = [e, n]"]]); [["X = [e, n]"]]);
assert_prolog_success!(&mut wam, "?- \"koen\" = [k, o | X], X = \"en\".", assert_prolog_success!(&mut wam, "?- \"koen\" = [k, o | X], X = \"en\".",
[["X = [e, n]"]]); [["X = [e, n]"]]);
assert_prolog_failure!(&mut wam, "?- \"koen\" = [k, o | X], X == \"en\"."); assert_prolog_success!(&mut wam, "?- \"koen\" = [k, o | X], X == \"en\".",
[["X = [e, n]"]]);
assert_prolog_success!(&mut wam, "?- \"koen\" = [k, o | X], X =@= \"en\".", assert_prolog_success!(&mut wam, "?- \"koen\" = [k, o | X], X =@= \"en\".",
[["X = [e, n]"]]); [["X = [e, n]"]]);
@@ -1843,7 +1844,8 @@ fn test_queries_on_string_lists()
assert_prolog_success!(&mut wam, "?- matcher(\"abcdef\", X), X = [d,e,f|Y], Y == [], X = \"def\".", assert_prolog_success!(&mut wam, "?- matcher(\"abcdef\", X), X = [d,e,f|Y], Y == [], X = \"def\".",
[["X = [d, e, f]", "Y = []"]]); [["X = [d, e, f]", "Y = []"]]);
assert_prolog_failure!(&mut wam, "?- matcher(\"abcdef\", X), X = [d,e,f|Y], Y == [], X == \"def\"."); assert_prolog_success!(&mut wam, "?- matcher(\"abcdef\", X), X = [d,e,f|Y], Y == [], X == \"def\".",
[["X = [d, e, f]", "Y = []"]]);
assert_prolog_success!(&mut wam, "?- X = ['a', 'b', 'c' | \"def\"].", assert_prolog_success!(&mut wam, "?- X = ['a', 'b', 'c' | \"def\"].",
[["X = [a, b, c, d, e, f]"]]); [["X = [a, b, c, d, e, f]"]]);
@@ -1860,4 +1862,43 @@ fn test_queries_on_string_lists()
assert_prolog_success!(&mut wam, "?- X = \"abc\", X = ['a' | Y], set_prolog_flag(double_quotes, atom).", assert_prolog_success!(&mut wam, "?- X = \"abc\", X = ['a' | Y], set_prolog_flag(double_quotes, atom).",
[["X = \"abc\"", "Y = \"bc\""]]); [["X = \"abc\"", "Y = \"bc\""]]);
// partial strings.
submit(&mut wam, "?- set_prolog_flag(double_quotes, chars).");
assert_prolog_failure!(&mut wam, "?- Y = 5, partial_string(\"abc\", Y).");
assert_prolog_success!(&mut wam, "?- partial_string(\"abc\", X).",
[["X = [a, b, c | _]"]]);
submit(&mut wam, "matcher([a, b, c | X], X).");
assert_prolog_success!(&mut wam, "?- partial_string(\"abc\", X), matcher(X, Y).",
[["X = [a, b, c | _]", "Y = _"]]);
assert_prolog_success!(&mut wam, "?- partial_string(\"abc\", X), matcher(X, Y), Y = \"def\".",
[["X = [a, b, c, d, e, f]", "Y = [d, e, f]"]]);
assert_prolog_success!(&mut wam, "?- partial_string(\"abc\", X), matcher(X, Y), \"def\" = Y.",
[["X = [a, b, c, d, e, f]", "Y = [d, e, f]"]]);
assert_prolog_success!(&mut wam, "?- partial_string(\"abc\", X), matcher(X, Y), partial_string(\"def\", Y),
Y = \"defghijkl\".",
[["X = [a, b, c, d, e, f, g, h, i, j, k, l]",
"Y = [d, e, f, g, h, i, j, k, l]"]]);
assert_prolog_success!(&mut wam, "?- partial_string(\"abc\", X), matcher(X, Y), partial_string(\"def\", Y),
\"defghijkl\" = Y.",
[["X = [a, b, c, d, e, f, g, h, i, j, k, l]",
"Y = [d, e, f, g, h, i, j, k, l]"]]);
assert_prolog_success!(&mut wam, "?- partial_string(\"abc\", X), matcher(X, Y), Y = [d, e, f | G].",
[["X = [a, b, c, d, e, f | _]", "Y = [d, e, f | _]", "G = _"]]);
assert_prolog_success!(&mut wam, "?- partial_string(\"abc\", X), matcher(X, Y), [d, e, f | G] = Y.",
[["X = [a, b, c, d, e, f | _]", "Y = [d, e, f | _]", "G = _"]]);
assert_prolog_success!(&mut wam, "?- partial_string(\"abc\", X), matcher(X, Y), Y = [d, e, f | G],
G = \"ghi\".",
[["X = [a, b, c, d, e, f, g, h, i]", "Y = [d, e, f, g, h, i]", "G = [g, h, i]"]]);
assert_prolog_success!(&mut wam, "?- partial_string(\"abc\", X), matcher(X, Y), Y = [d, e, f | G],
is_partial_string(Y), G = \"ghi\".",
[["X = [a, b, c, d, e, f, g, h, i]", "Y = [d, e, f, g, h, i]", "G = [g, h, i]"]]);
assert_prolog_success!(&mut wam, "?- partial_string(\"abc\", X), matcher(X, Y), Y = [d, e, f | G],
is_partial_string(Y), is_partial_string(G), G = \"ghi\".",
[["X = [a, b, c, d, e, f, g, h, i]", "Y = [d, e, f, g, h, i]", "G = [g, h, i]"]]);
} }