* Default representation of strings as list of chars, using a packed
internal representation (_done_).
- A representation of 'partial strings' as difference lists
- of characters (_in progress_).
+ of characters (_done_).
* `term_expansion/2` and `goal_expansion/2` (_in progress_).
* Definite Clause Grammars.
* Attributed variables using the SICStus Prolog interface and
* `ground/1`
* `integer/1`
* `is_list/1`
+* `is_partial_string/1`
* `keysort/2`
* `length/2`
* `maplist/2..9`
* `memberchk/2`
* `nonvar/1`
* `once/1`
+* `partial_string/2`
* `rational/1`
* `read/1`
* `repeat/0`
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
rusty-wam has a simple predicate-based module system. It provides a
(&Constant::Atom(ref atom), &Constant::Char(c))
| (&Constant::Char(c), &Constant::Atom(ref atom)) => {
let s = atom.as_str();
- if c.len_utf8() != s.len() || Some(c) != s.chars().next() {
- false
- } else {
- true
- }
+ c.len_utf8() == s.len() && Some(c) == s.chars().next()
},
(&Constant::Atom(ref a1), &Constant::Atom(ref a2)) =>
a1.as_str() == a2.as_str(),
#[derive(Clone, PartialEq)]
pub enum InlinedClauseType {
- CompareNumber(CompareNumberQT, ArithmeticTerm, ArithmeticTerm),
+ CompareNumber(CompareNumberQT, ArithmeticTerm, ArithmeticTerm),
IsAtom(RegType),
IsAtomic(RegType),
IsCompound(RegType),
IsString(RegType),
IsFloat(RegType),
IsNonVar(RegType),
- IsVar(RegType),
+ IsPartialString(RegType),
+ IsVar(RegType)
}
impl InlinedClauseType {
&InlinedClauseType::IsString(..) => "string",
&InlinedClauseType::IsFloat (..) => "float",
&InlinedClauseType::IsNonVar(..) => "nonvar",
- &InlinedClauseType::IsVar(..) => "var"
+ &InlinedClauseType::IsPartialString(..) => "partial_string",
+ &InlinedClauseType::IsVar(..) => "var",
}
}
("float", 1) => Some(InlinedClauseType::IsFloat(r1)),
("nonvar", 1) => Some(InlinedClauseType::IsNonVar(r1)),
("var", 1) => Some(InlinedClauseType::IsVar(r1)),
+ ("is_partial_string", 1) => Some(InlinedClauseType::IsPartialString(r1)),
_ => None
}
}
#[derive(Clone, PartialEq)]
pub enum BuiltInClauseType {
AcyclicTerm,
- Arg,
+ Arg,
Compare,
CompareTerm(CompareTermQT),
CyclicTerm,
Is(RegType, ArithmeticTerm),
KeySort,
NotEq,
+ PartialString,
Read,
Sort,
}
&BuiltInClauseType::NotEq => clause_name!("\\=="),
&BuiltInClauseType::Read => clause_name!("read"),
&BuiltInClauseType::Sort => clause_name!("sort"),
+ &BuiltInClauseType::PartialString => clause_name!("partial_string")
}
}
&BuiltInClauseType::NotEq => 2,
&BuiltInClauseType::Read => 1,
&BuiltInClauseType::Sort => 2,
+ &BuiltInClauseType::PartialString => 1,
}
}
("keysort", 2) => Some(BuiltInClauseType::KeySort),
("\\==", 2) => Some(BuiltInClauseType::NotEq),
("sort", 2) => Some(BuiltInClauseType::Sort),
- ("read", 1) => Some(BuiltInClauseType::Read),
+ ("read", 1) => Some(BuiltInClauseType::Read),
_ => None
}
}
NumberPair::Float(n1, n2) =>
pow_float(n1.into_inner(), n2.into_inner()),
NumberPair::Rational(r1, r2) => {
- if let (Some(f1), Some(f2)) = (rational_to_f64(&r1), rational_to_f64(&r2)) {
+ if let (Some(f1), Some(f2)) = (rational_to_f64(&r1), rational_to_f64(&r2)) {
if let Ok(result) = pow_float(f1, f2) {
return Ok(result);
}
}
-
+
let root = rational_pow((*r1).clone(), (*r2).clone())?;
Ok(Number::Rational(Rc::new(root)))
- }
+ }
}
}
*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)
-> RegType
{
};
}
- fn compile_target<Target, Iter>(&mut self, iter: Iter, term_loc: GenContext, is_exposed: bool)
- -> Vec<Target>
+ fn compile_target<Target, Iter>(&mut self, iter: Iter, term_loc: GenContext, is_exposed: bool) -> Vec<Target>
where Target: CompilationTarget<'a>, Iter: Iterator<Item=TermRef<'a>>
{
let mut target = Vec::new();
let r = self.mark_non_callable(name.clone(), 1, term_loc, vr, code);
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!())
}
}
code: &mut Code, is_exposed: bool)
-> Result<(), ParserError>
{
- for (chunk_num, _, terms) in iter.rule_body_iter()
- {
- for (i, term) in terms.iter().enumerate()
- {
+ for (chunk_num, _, terms) in iter.rule_body_iter() {
+ for (i, term) in terms.iter().enumerate() {
let term_loc = if i + 1 < terms.len() {
GenContext::Mid(chunk_num)
} else {
code.push(fail!());
}
}
- },
+ },
&QueryTerm::Clause(_, ClauseType::Inlined(ref ct), ref terms, _) =>
try!(self.compile_inlined(ct, terms, term_loc, code)),
_ => {
// add a proceed to bookend any trailing cuts.
match toc {
&QueryTerm::BlockedCut | &QueryTerm::UnblockedCut(..) => code.push(proceed!()),
- &QueryTerm::Clause(_, ClauseType::Inlined(..), ..) => code.push(proceed!()),
+ &QueryTerm::Clause(_, ClauseType::Inlined(..), ..) => code.push(proceed!()),
_ => {}
};
let atom_tbl = wam.atom_tbl();
let string_tbl = wam.string_tbl();
let flags = wam.machine_flags();
-
+
let index = MachineCodeIndices {
code_dir: &mut wam.code_dir,
op_dir: &mut wam.op_dir,
};
-
+
let mut worker = TopLevelWorker::new(buffer.as_bytes(), atom_tbl, string_tbl,
flags, index);
worker.parse_code()
-> Result<(Code, AllocVarDict), ParserError>
{
// count backtracking inferences.
- let mut cg = CodeGenerator::<DebrayAllocator>::new(false, flags);
+ let mut cg = CodeGenerator::<DebrayAllocator>::new(false, flags);
let mut code = try!(cg.compile_query(&terms));
compile_appendix(&mut code, queue, false, flags)?;
-
+
Ok((code, cg.take_vars()))
}
compile_appendix(&mut decl_code, Vec::from(queue), non_counted_bt,
self.wam.machine_flags())?;
-
+
let idx = code_dir.entry((name, arity)).or_insert(CodeIndex::default());
set_code_index!(idx, IndexPtr::Index(p), self.get_module_name());
while let Some(decl) = try_eval_session!(worker.consume(&mut indices)) {
match decl {
Declaration::NonCountedBacktracking(name, arity) =>
- compiler.add_non_counted_bt_flag(name, arity),
+ compiler.add_non_counted_bt_flag(name, arity),
Declaration::Op(op_decl) =>
try_eval_session!(op_decl.submit(compiler.get_module_name(), &mut indices.op_dir)),
Declaration::UseModule(name) =>
if self.machine_st.machine_flags().double_quotes.is_chars() => {
if let Some(c) = s.head() {
let tail = s.tail();
-
+
self.state_stack.push(Addr::Con(Constant::String(tail)));
self.state_stack.push(Addr::Con(Constant::Char(c)));
}
HCPostOrderIterator::new(HCPreOrderIterator::new(self, a))
}
- pub fn acyclic_pre_order_iter<'a>(&'a self, a: Addr)
- -> HCAcyclicIterator<HCPreOrderIterator<'a>>
+ pub fn acyclic_pre_order_iter<'a>(&'a self, a: Addr) -> HCAcyclicIterator<HCPreOrderIterator<'a>>
{
HCAcyclicIterator::new(HCPreOrderIterator::new(self, a))
}
if !self.seen.contains(&addr) {
self.iter.stack().push(addr.clone());
self.seen.insert(addr);
+
break;
}
}
if self.machine_st.machine_flags().double_quotes.is_chars() {
if !s.is_empty() {
self.push_list();
+ } else if s.is_expandable() {
+ if !self.at_cdr(" | _") {
+ self.outputter.push_char('_');
+ }
} else if !self.at_cdr("") {
self.outputter.append("[]");
}
- // self.expand_char_list(s);
} else { // for now, == DoubleQuotes::Atom
let borrowed_str = s.borrow();
let state = TermIterState::Clause(Level::Root, 0, cell, ct.clone(), terms);
QueryIterator { state_stack: vec![state] }
},
- &QueryTerm::BlockedCut =>
- QueryIterator { state_stack: vec![] },
&QueryTerm::UnblockedCut(ref cell) => {
let state = TermIterState::Var(Level::Root, cell, rc_atom!("!"));
QueryIterator { state_stack: vec![state] }
}).collect();
QueryIterator { state_stack }
- }
+ },
+ &QueryTerm::BlockedCut =>
+ QueryIterator { state_stack: vec![] },
}
}
}
result.push(term),
ChunkedTerm::BodyTerm(&QueryTerm::Clause(_, ClauseType::Inlined(_), ..)) =>
result.push(term),
- ChunkedTerm::BodyTerm(&QueryTerm::Clause(_, ClauseType::CallN, ref subterms, _)) =>
- {
+ ChunkedTerm::BodyTerm(&QueryTerm::Clause(_, ClauseType::CallN, ref subterms, _)) => {
result.push(term);
arity = subterms.len() + 1;
break;
code_dirs: CodeDirs)
-> CallResult
{
- match ct {
+ match ct {
&BuiltInClauseType::AcyclicTerm => {
let addr = machine_st[temp_v!(1)].clone();
machine_st.fail = machine_st.is_cyclic_term(addr);
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 => {
machine_st.check_sort_errors()?;
use prolog::num::bigint::{BigInt, BigUint};
use prolog::num::rational::Ratio;
use prolog::or_stack::*;
+use prolog::string_list::StringList;
use std::cell::RefCell;
use std::cmp::{max, Ordering};
self.fail = true;
},
- (Addr::Lis(a1), Addr::Con(Constant::String(ref s)))
- | (Addr::Con(Constant::String(ref s)), Addr::Lis(a1))
- if self.flags.double_quotes.is_chars() =>
+ (Addr::Lis(a1), Addr::Con(Constant::String(ref mut s)))
+ | (Addr::Con(Constant::String(ref mut s)), Addr::Lis(a1))
+ if self.flags.double_quotes.is_chars() => {
if let Some(c) = s.head() {
pdl.push(Addr::Con(Constant::String(s.tail())));
pdl.push(Addr::HeapCell(a1 + 1));
pdl.push(Addr::Con(Constant::Char(c)));
pdl.push(Addr::HeapCell(a1));
- } else {
- self.fail = true;
- },
+
+ 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;
+ },
(Addr::Con(Constant::EmptyList), Addr::Con(Constant::String(ref s)))
| (Addr::Con(Constant::String(ref s)), Addr::Con(Constant::EmptyList))
if self.flags.double_quotes.is_chars() => {
+ if s.is_expandable() && s.is_empty() {
+ s.set_non_expandable();
+ continue;
+ }
+
self.fail = !s.is_empty();
},
(Addr::Lis(a1), Addr::Lis(a2)) => {
pdl.push(Addr::HeapCell(a1 + 1));
pdl.push(Addr::HeapCell(a2 + 1));
},
- (Addr::Con(Constant::String(ref s1)), Addr::Con(Constant::String(ref s2))) => {
- if let Some(c1) = s1.head() {
- if let Some(c2) = s2.head() {
- if c1 == c2 {
+ (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(c2) = s2.head() {
+ if c1 == c2 {
+ pdl.push(Addr::Con(Constant::String(s1.tail())));
+ pdl.push(Addr::Con(Constant::String(s2.tail())));
+
+ 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())));
- pdl.push(Addr::Con(Constant::String(s2.tail())));
- continue;
+ return true;
}
+ } else if s1.is_expandable() {
+ if let Some(c) = s2.head() {
+ 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;
}
- } else {
- if s2.head().is_none() {
- continue;
- }
- }
- self.fail = true;
+ false
+ };
+
+ self.fail = !(stepper(s1, s2) || stepper(s2, s1));
},
(Addr::Con(ref c1), Addr::Con(ref c2)) =>
if c1 != c2 {
}
pub(super) fn write_constant_to_var(&mut self, addr: Addr, c: Constant) {
- let addr = self.deref(addr);
-
- match self.store(addr) {
+ match self.store(self.deref(addr)) {
Addr::HeapCell(hc) => {
self.heap[hc] = HeapCellValue::Addr(Addr::Con(c.clone()));
self.trail(Ref::HeapCell(hc));
self.and_stack[fr][sc] = Addr::Con(c.clone());
self.trail(Ref::StackCell(fr, sc));
},
- Addr::Con(Constant::String(s)) =>
+ Addr::Con(Constant::String(ref mut s)) =>
self.fail = match c {
- Constant::EmptyList
- if self.flags.double_quotes.is_chars() =>
+ Constant::EmptyList if self.flags.double_quotes.is_chars() =>
!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
},
Addr::Con(c1) => {
}
pub(super) fn execute_arith_instr(&mut self, instr: &ArithmeticInstruction) {
- match instr {
+ match instr {
&ArithmeticInstruction::Add(ref a1, ref a2, t) => {
let n1 = try_or_fail!(self, self.get_number(a1));
let n2 = try_or_fail!(self, self.get_number(a2));
match addr {
Addr::Con(Constant::String(ref s))
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::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.mode = MachineMode::Read;
} else {
_ => 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
+ };
+ }
}
}
while self.ms.p < end_ptr {
if let CodePtr::Local(LocalCodePtr::TopLevel(mut cn, p)) = self.ms.p {
match &self[LocalCodePtr::TopLevel(cn, p)] {
- &Line::Control(ref ctrl_instr) if ctrl_instr.is_jump_instr() => {
+ &Line::Control(ref ctrl_instr) if ctrl_instr.is_jump_instr() => {
self.record_var_places(cn, alloc_locs, heap_locs);
cn += 1;
},
);
($name:expr, $len:expr, [$($args:expr),*], $fix: expr) => (
vec![ HeapCellValue::NamedStr($len, clause_name!($name), Some($fix)), $($args),* ]
- );
+ );
}
macro_rules! temp_v {
)
}
+macro_rules! is_partial_string {
+ ($r:expr) => (
+ call_clause!(ClauseType::Inlined(InlinedClauseType::IsPartialString($r)), 1, 0)
+ )
+}
+
macro_rules! call_clause {
($ct:expr, $arity:expr, $pvs:expr) => (
Line::Control(ControlInstruction::CallClause($ct, $arity, $pvs, false, false))
impl Ord for StringList {
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 {
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
}
}
}
}
+ #[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]
pub fn cursor(&self) -> usize {
self.cursor
self.fabricate_rule(fold_by_str(prec_seq, body_term, comma_sym))
}
- fn to_query_term(&mut self, indices: &mut MachineCodeIndices, term: Term)
- -> Result<QueryTerm, ParserError>
+ fn to_query_term(&mut self, indices: &mut MachineCodeIndices, term: Term) -> Result<QueryTerm, ParserError>
{
match term {
Term::Constant(r, Constant::Atom(name)) =>
Term::Var(_, ref v) if v.as_str() == "!" =>
Ok(QueryTerm::UnblockedCut(Cell::default())),
Term::Clause(r, name, mut terms, fixity) =>
- if name.as_str() == ";" && terms.len() == 2 {
- let term = Term::Clause(r, name.clone(), terms, fixity);
- let (stub, clauses) = self.fabricate_disjunct(term);
-
- self.queue.push_back(clauses);
- Ok(QueryTerm::Jump(stub))
- } else if name.as_str() == ":" && terms.len() == 2 {
- let callee = *terms.pop().unwrap();
- let mod_name = *terms.pop().unwrap();
-
- module_resolution_call(mod_name, callee)
- } else if name.as_str() == "->" && terms.len() == 2 {
- let conq = *terms.pop().unwrap();
- let prec = *terms.pop().unwrap();
-
- let (stub, clauses) = self.fabricate_if_then(prec, conq);
-
- self.queue.push_back(clauses);
- Ok(QueryTerm::Jump(stub))
- } else if name.as_str() == "$get_level" && terms.len() == 1 {
- if let Term::Var(_, ref var) = *terms[0] {
- Ok(QueryTerm::GetLevelAndUnify(Cell::default(), var.clone()))
- } else {
+ match (name.as_str(), terms.len()) {
+ (";", 2) => {
+ let term = Term::Clause(r, name.clone(), terms, fixity);
+ let (stub, clauses) = self.fabricate_disjunct(term);
+
+ self.queue.push_back(clauses);
+ Ok(QueryTerm::Jump(stub))
+ },
+ (":", 2) => {
+ let callee = *terms.pop().unwrap();
+ let mod_name = *terms.pop().unwrap();
+
+ module_resolution_call(mod_name, callee)
+ },
+ ("->", 2) => {
+ let conq = *terms.pop().unwrap();
+ let prec = *terms.pop().unwrap();
+
+ let (stub, clauses) = self.fabricate_if_then(prec, conq);
+
+ self.queue.push_back(clauses);
+ Ok(QueryTerm::Jump(stub))
+ },
+ ("$get_level", 1) =>
+ if let Term::Var(_, ref var) = *terms[0] {
+ Ok(QueryTerm::GetLevelAndUnify(Cell::default(), var.clone()))
+ } else {
+ 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));
+ }
+ }
+
Err(ParserError::InadmissibleQueryTerm)
+ },
+ _ => {
+ let ct = indices.lookup(name, terms.len(), fixity);
+ Ok(QueryTerm::Clause(Cell::default(), ct, terms, false))
}
- } else {
- let ct = indices.lookup(name, terms.len(), fixity);
- Ok(QueryTerm::Clause(Cell::default(), ct, terms, false))
},
Term::Var(..) =>
- Ok(QueryTerm::Clause(Cell::default(), ClauseType::CallN, vec![Box::new(term)],
- false)),
- _ =>
- Err(ParserError::InadmissibleQueryTerm)
+ Ok(QueryTerm::Clause(Cell::default(), ClauseType::CallN, vec![Box::new(term)], false)),
+ _ => Err(ParserError::InadmissibleQueryTerm)
}
}
assert_prolog_success!(&mut wam, "?- f(5, 33).");
assert_prolog_failure!(&mut wam, "?- f(5, 32).");
- // exponentiation.
+ // exponentiation.
// the ~ operators tests whether |X - Y| <= 1/10000...
// or whatever degree of approximation used by Newton's method in rational_pow.
assert_prolog_success!(&mut wam, "?- X is (1 rdiv 3) ^ 0.5, Y is X ^ 2, 1 rdiv 3 ~ Y.");
assert_prolog_success!(&mut wam, "?- X is (-5) ^ (-1 rdiv 3), Y is X ^ 3, Y ~ -1 rdiv 5.");
- assert_prolog_failure!(&mut wam, "?- X is (-5) ^ (-1 rdiv 3), Y is X ^ 3, Y ~ 1 rdiv 5.");
-
+ assert_prolog_failure!(&mut wam, "?- X is (-5) ^ (-1 rdiv 3), Y is X ^ 3, Y ~ 1 rdiv 5.");
+
assert_prolog_success!(&mut wam, "?- X is (0 rdiv 5) ^ 5.",
[["X = 0"]]);
assert_prolog_success!(&mut wam, "?- X is (-0 rdiv 5) ^ 5.",
[["X = [e, n]"]]);
assert_prolog_success!(&mut wam, "?- \"koen\" = [k, o | X], X = \"en\".",
[["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\".",
[["X = [e, n]"]]);
assert_prolog_success!(&mut wam, "?- matcher(\"abcdef\", X), X = [d,e,f|Y], Y == [], X = \"def\".",
[["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\"].",
[["X = [a, b, c, d, e, f]"]]);
assert_prolog_success!(&mut wam, "?- X = \"abc\", X = ['a' | Y], set_prolog_flag(double_quotes, atom).",
[["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]"]]);
}