finalize support for partial strings
This commit is contained in:
23
README.md
23
README.md
@@ -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
|
||||||
|
|||||||
@@ -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(),
|
||||||
@@ -597,7 +593,7 @@ pub enum Term {
|
|||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum InlinedClauseType {
|
pub enum InlinedClauseType {
|
||||||
CompareNumber(CompareNumberQT, ArithmeticTerm, ArithmeticTerm),
|
CompareNumber(CompareNumberQT, ArithmeticTerm, ArithmeticTerm),
|
||||||
IsAtom(RegType),
|
IsAtom(RegType),
|
||||||
IsAtomic(RegType),
|
IsAtomic(RegType),
|
||||||
IsCompound(RegType),
|
IsCompound(RegType),
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -846,7 +845,7 @@ impl SystemClauseType {
|
|||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum BuiltInClauseType {
|
pub enum BuiltInClauseType {
|
||||||
AcyclicTerm,
|
AcyclicTerm,
|
||||||
Arg,
|
Arg,
|
||||||
Compare,
|
Compare,
|
||||||
CompareTerm(CompareTermQT),
|
CompareTerm(CompareTermQT),
|
||||||
CyclicTerm,
|
CyclicTerm,
|
||||||
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1007,7 +1009,7 @@ impl BuiltInClauseType {
|
|||||||
("keysort", 2) => Some(BuiltInClauseType::KeySort),
|
("keysort", 2) => Some(BuiltInClauseType::KeySort),
|
||||||
("\\==", 2) => Some(BuiltInClauseType::NotEq),
|
("\\==", 2) => Some(BuiltInClauseType::NotEq),
|
||||||
("sort", 2) => Some(BuiltInClauseType::Sort),
|
("sort", 2) => Some(BuiltInClauseType::Sort),
|
||||||
("read", 1) => Some(BuiltInClauseType::Read),
|
("read", 1) => Some(BuiltInClauseType::Read),
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1291,15 +1293,15 @@ impl Number {
|
|||||||
NumberPair::Float(n1, n2) =>
|
NumberPair::Float(n1, n2) =>
|
||||||
pow_float(n1.into_inner(), n2.into_inner()),
|
pow_float(n1.into_inner(), n2.into_inner()),
|
||||||
NumberPair::Rational(r1, r2) => {
|
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) {
|
if let Ok(result) = pow_float(f1, f2) {
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let root = rational_pow((*r1).clone(), (*r2).clone())?;
|
let root = rational_pow((*r1).clone(), (*r2).clone())?;
|
||||||
Ok(Number::Rational(Rc::new(root)))
|
Ok(Number::Rational(Rc::new(root)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
@@ -473,7 +478,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker>
|
|||||||
code.push(fail!());
|
code.push(fail!());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
&QueryTerm::Clause(_, ClauseType::Inlined(ref ct), ref terms, _) =>
|
&QueryTerm::Clause(_, ClauseType::Inlined(ref ct), ref terms, _) =>
|
||||||
try!(self.compile_inlined(ct, terms, term_loc, code)),
|
try!(self.compile_inlined(ct, terms, term_loc, code)),
|
||||||
_ => {
|
_ => {
|
||||||
@@ -512,7 +517,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker>
|
|||||||
// add a proceed to bookend any trailing cuts.
|
// add a proceed to bookend any trailing cuts.
|
||||||
match toc {
|
match toc {
|
||||||
&QueryTerm::BlockedCut | &QueryTerm::UnblockedCut(..) => code.push(proceed!()),
|
&QueryTerm::BlockedCut | &QueryTerm::UnblockedCut(..) => code.push(proceed!()),
|
||||||
&QueryTerm::Clause(_, ClauseType::Inlined(..), ..) => code.push(proceed!()),
|
&QueryTerm::Clause(_, ClauseType::Inlined(..), ..) => code.push(proceed!()),
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -41,12 +41,12 @@ pub fn parse_code(wam: &mut Machine, buffer: &str) -> Result<TopLevelPacket, Par
|
|||||||
let atom_tbl = wam.atom_tbl();
|
let atom_tbl = wam.atom_tbl();
|
||||||
let string_tbl = wam.string_tbl();
|
let string_tbl = wam.string_tbl();
|
||||||
let flags = wam.machine_flags();
|
let flags = wam.machine_flags();
|
||||||
|
|
||||||
let index = MachineCodeIndices {
|
let index = MachineCodeIndices {
|
||||||
code_dir: &mut wam.code_dir,
|
code_dir: &mut wam.code_dir,
|
||||||
op_dir: &mut wam.op_dir,
|
op_dir: &mut wam.op_dir,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut worker = TopLevelWorker::new(buffer.as_bytes(), atom_tbl, string_tbl,
|
let mut worker = TopLevelWorker::new(buffer.as_bytes(), atom_tbl, string_tbl,
|
||||||
flags, index);
|
flags, index);
|
||||||
worker.parse_code()
|
worker.parse_code()
|
||||||
@@ -102,11 +102,11 @@ fn compile_query(terms: Vec<QueryTerm>, queue: Vec<TopLevel>, flags: MachineFlag
|
|||||||
-> Result<(Code, AllocVarDict), ParserError>
|
-> Result<(Code, AllocVarDict), ParserError>
|
||||||
{
|
{
|
||||||
// count backtracking inferences.
|
// 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));
|
let mut code = try!(cg.compile_query(&terms));
|
||||||
|
|
||||||
compile_appendix(&mut code, queue, false, flags)?;
|
compile_appendix(&mut code, queue, false, flags)?;
|
||||||
|
|
||||||
Ok((code, cg.take_vars()))
|
Ok((code, cg.take_vars()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,7 +193,7 @@ impl<'a> ListingCompiler<'a> {
|
|||||||
|
|
||||||
compile_appendix(&mut decl_code, Vec::from(queue), non_counted_bt,
|
compile_appendix(&mut decl_code, Vec::from(queue), non_counted_bt,
|
||||||
self.wam.machine_flags())?;
|
self.wam.machine_flags())?;
|
||||||
|
|
||||||
let idx = code_dir.entry((name, arity)).or_insert(CodeIndex::default());
|
let idx = code_dir.entry((name, arity)).or_insert(CodeIndex::default());
|
||||||
set_code_index!(idx, IndexPtr::Index(p), self.get_module_name());
|
set_code_index!(idx, IndexPtr::Index(p), self.get_module_name());
|
||||||
|
|
||||||
@@ -252,7 +252,7 @@ fn compile_listing(wam: &mut Machine, src_str: &str, mut indices: MachineCodeInd
|
|||||||
while let Some(decl) = try_eval_session!(worker.consume(&mut indices)) {
|
while let Some(decl) = try_eval_session!(worker.consume(&mut indices)) {
|
||||||
match decl {
|
match decl {
|
||||||
Declaration::NonCountedBacktracking(name, arity) =>
|
Declaration::NonCountedBacktracking(name, arity) =>
|
||||||
compiler.add_non_counted_bt_flag(name, arity),
|
compiler.add_non_counted_bt_flag(name, arity),
|
||||||
Declaration::Op(op_decl) =>
|
Declaration::Op(op_decl) =>
|
||||||
try_eval_session!(op_decl.submit(compiler.get_module_name(), &mut indices.op_dir)),
|
try_eval_session!(op_decl.submit(compiler.get_module_name(), &mut indices.op_dir)),
|
||||||
Declaration::UseModule(name) =>
|
Declaration::UseModule(name) =>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ impl<'a> HCPreOrderIterator<'a> {
|
|||||||
if self.machine_st.machine_flags().double_quotes.is_chars() => {
|
if self.machine_st.machine_flags().double_quotes.is_chars() => {
|
||||||
if let Some(c) = s.head() {
|
if let Some(c) = s.head() {
|
||||||
let tail = s.tail();
|
let tail = s.tail();
|
||||||
|
|
||||||
self.state_stack.push(Addr::Con(Constant::String(tail)));
|
self.state_stack.push(Addr::Con(Constant::String(tail)));
|
||||||
self.state_stack.push(Addr::Con(Constant::Char(c)));
|
self.state_stack.push(Addr::Con(Constant::Char(c)));
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -553,7 +553,7 @@ pub(crate) trait CallPolicy: Any {
|
|||||||
code_dirs: CodeDirs)
|
code_dirs: CodeDirs)
|
||||||
-> CallResult
|
-> CallResult
|
||||||
{
|
{
|
||||||
match ct {
|
match ct {
|
||||||
&BuiltInClauseType::AcyclicTerm => {
|
&BuiltInClauseType::AcyclicTerm => {
|
||||||
let addr = machine_st[temp_v!(1)].clone();
|
let addr = machine_st[temp_v!(1)].clone();
|
||||||
machine_st.fail = machine_st.is_cyclic_term(addr);
|
machine_st.fail = machine_st.is_cyclic_term(addr);
|
||||||
@@ -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()?;
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
|
||||||
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::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))) => {
|
||||||
if let Some(c1) = s1.head() {
|
let mut stepper = |s1: &mut StringList, s2: &mut StringList| -> bool {
|
||||||
if let Some(c2) = s2.head() {
|
if let Some(c1) = s1.head() {
|
||||||
if c1 == c2 {
|
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(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)) =>
|
(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) => {
|
||||||
@@ -691,7 +742,7 @@ impl MachineState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn execute_arith_instr(&mut self, instr: &ArithmeticInstruction) {
|
pub(super) fn execute_arith_instr(&mut self, instr: &ArithmeticInstruction) {
|
||||||
match instr {
|
match instr {
|
||||||
&ArithmeticInstruction::Add(ref a1, ref a2, t) => {
|
&ArithmeticInstruction::Add(ref a1, ref a2, t) => {
|
||||||
let n1 = try_or_fail!(self, self.get_number(a1));
|
let n1 = try_or_fail!(self, self.get_number(a1));
|
||||||
let n2 = try_or_fail!(self, self.get_number(a2));
|
let n2 = try_or_fail!(self, self.get_number(a2));
|
||||||
@@ -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
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -408,7 +408,7 @@ impl Machine {
|
|||||||
while self.ms.p < end_ptr {
|
while self.ms.p < end_ptr {
|
||||||
if let CodePtr::Local(LocalCodePtr::TopLevel(mut cn, p)) = self.ms.p {
|
if let CodePtr::Local(LocalCodePtr::TopLevel(mut cn, p)) = self.ms.p {
|
||||||
match &self[LocalCodePtr::TopLevel(cn, 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);
|
self.record_var_places(cn, alloc_locs, heap_locs);
|
||||||
cn += 1;
|
cn += 1;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ macro_rules! functor {
|
|||||||
);
|
);
|
||||||
($name:expr, $len:expr, [$($args:expr),*], $fix: expr) => (
|
($name:expr, $len:expr, [$($args:expr),*], $fix: expr) => (
|
||||||
vec![ HeapCellValue::NamedStr($len, clause_name!($name), Some($fix)), $($args),* ]
|
vec![ HeapCellValue::NamedStr($len, clause_name!($name), Some($fix)), $($args),* ]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! temp_v {
|
macro_rules! temp_v {
|
||||||
@@ -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))
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,40 +421,53 @@ 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()) {
|
||||||
let term = Term::Clause(r, name.clone(), terms, fixity);
|
(";", 2) => {
|
||||||
let (stub, clauses) = self.fabricate_disjunct(term);
|
let term = Term::Clause(r, name.clone(), terms, fixity);
|
||||||
|
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 {
|
},
|
||||||
let callee = *terms.pop().unwrap();
|
(":", 2) => {
|
||||||
let mod_name = *terms.pop().unwrap();
|
let callee = *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 {
|
},
|
||||||
let conq = *terms.pop().unwrap();
|
("->", 2) => {
|
||||||
let prec = *terms.pop().unwrap();
|
let conq = *terms.pop().unwrap();
|
||||||
|
let prec = *terms.pop().unwrap();
|
||||||
|
|
||||||
let (stub, clauses) = self.fabricate_if_then(prec, conq);
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
|
||||||
Err(ParserError::InadmissibleQueryTerm)
|
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(..) =>
|
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
51
src/tests.rs
51
src/tests.rs
@@ -1020,7 +1020,7 @@ fn test_queries_on_arithmetic()
|
|||||||
assert_prolog_success!(&mut wam, "?- f(5, 33).");
|
assert_prolog_success!(&mut wam, "?- f(5, 33).");
|
||||||
assert_prolog_failure!(&mut wam, "?- f(5, 32).");
|
assert_prolog_failure!(&mut wam, "?- f(5, 32).");
|
||||||
|
|
||||||
// exponentiation.
|
// exponentiation.
|
||||||
|
|
||||||
// the ~ operators tests whether |X - Y| <= 1/10000...
|
// the ~ operators tests whether |X - Y| <= 1/10000...
|
||||||
// or whatever degree of approximation used by Newton's method in rational_pow.
|
// or whatever degree of approximation used by Newton's method in rational_pow.
|
||||||
@@ -1078,8 +1078,8 @@ fn test_queries_on_arithmetic()
|
|||||||
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 (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_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.",
|
assert_prolog_success!(&mut wam, "?- X is (0 rdiv 5) ^ 5.",
|
||||||
[["X = 0"]]);
|
[["X = 0"]]);
|
||||||
assert_prolog_success!(&mut wam, "?- X is (-0 rdiv 5) ^ 5.",
|
assert_prolog_success!(&mut wam, "?- X is (-0 rdiv 5) ^ 5.",
|
||||||
@@ -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]"]]);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user