address issues #143, #150, #155

This commit is contained in:
Mark Thom
2019-09-02 16:53:31 -04:00
parent 07412b9d34
commit 2c880b2bc0
25 changed files with 314 additions and 210 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "scryer-prolog"
version = "0.8.86"
version = "0.8.87"
authors = ["Mark Thom <markjordanthom@gmail.com>"]
repository = "https://github.com/mthom/scryer-prolog"
description = "A modern Prolog implementation written mostly in Rust."

View File

@@ -359,16 +359,12 @@ been exported to the toplevel. To export them, write
?- use_module(library(lists)).
```
The [user] prompt can also be used to define modules inline at the
REPL:
To load modules contained in files, the `library` functor can be
omitted, prompting Scryer to search for the file (specified as an
atom) from its home directory:
```
?- [user].
(type Enter + Ctrl-D to terminate the stream when finished)
:- module(test, [local_member/2]).
:- use_module(library(lists)).
local_member(X, Xs) :- member(X, Xs).
?- use_module('file.pl').
```
`use_module` directives can be qualified by adding a list of imports:
@@ -386,3 +382,18 @@ imported to the current working namespace:
```
?- lists:member(X, Xs).
```
The [user] prompt can also be used to define modules inline at the
REPL:
```
?- [user].
(type Enter + Ctrl-D to terminate the stream when finished)
:- module(test, [local_member/2]).
:- use_module(library(lists)).
local_member(X, Xs) :- member(X, Xs).
```
The user listing can also be terminated by placing `end_of_file.` at
the end of the stream.

View File

@@ -15,7 +15,6 @@ extern crate termion;
mod prolog;
use prolog::machine::*;
use prolog::machine::machine_errors::*;
use prolog::read::*;
#[cfg(test)]

View File

@@ -50,6 +50,15 @@ impl TopLevel {
&TopLevel::Rule(Rule { ref head, .. }) => head.1.len()
}
}
pub fn is_end_of_file_atom(&self) -> bool {
match self {
&TopLevel::Fact(Term::Constant(_, Constant::Atom(ref name, _))) =>
return name.as_str() == "end_of_file",
_ =>
false
}
}
}
#[derive(Clone, Copy)]
@@ -152,15 +161,22 @@ impl PredicateClause {
}
}
#[derive(Clone)]
pub enum ModuleSource {
Library(ClauseName),
File(ClauseName)
}
#[derive(Clone)]
pub enum Declaration {
Dynamic(ClauseName, usize), // name, arity
EndOfFile,
Hook(CompileTimeHook, PredicateClause, VecDeque<TopLevel>),
Module(ModuleDecl),
NonCountedBacktracking(ClauseName, usize), // name, arity
Op(OpDecl),
UseModule(ClauseName),
UseQualifiedModule(ClauseName, Vec<PredicateKey>)
UseModule(ModuleSource),
UseQualifiedModule(ModuleSource, Vec<PredicateKey>)
}
impl Declaration {
@@ -168,6 +184,11 @@ impl Declaration {
pub fn is_module_decl(&self) -> bool {
if let &Declaration::Module(_) = self { true } else { false }
}
#[inline]
pub fn is_end_of_file(&self) -> bool {
if let &Declaration::EndOfFile = self { true } else { false }
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]

View File

@@ -52,7 +52,7 @@
del_max_assoc/4 % +Assoc0, ?Key, ?Value, ?Assoc
]).
:- use_module(library(lists)).
:- use_module('src/prolog/lib/lists.pl').
/** <module> Binary associations

View File

@@ -4,8 +4,8 @@
'$add_to_list'/3, '$del_attr'/3, '$del_attr_step'/3,
'$del_attr_buried'/4]).
:- use_module(library(dcgs)).
:- use_module(library(terms)).
:- use_module('src/prolog/lib/dcgs.pl').
:- use_module('src/prolog/lib/terms.pl').
:- op(1199, fx, attribute).

View File

@@ -2,8 +2,8 @@
%% TODO: numlist/5.
:- use_module(library(lists), [length/2]).
:- use_module(library(error)).
:- use_module('src/prolog/lib/lists.pl', [length/2]).
:- use_module('src/prolog/lib/error.pl').
between(Lower, Upper, X) :-
must_be(integer, Lower),

View File

@@ -2,8 +2,8 @@
:- module(dcgs, [phrase/2, phrase/3]).
:- use_module(library(lists), [append/3]).
:- use_module(library(terms)).
:- use_module('src/prolog/lib/lists.pl', [append/3]).
:- use_module('src/prolog/lib/terms.pl').
phrase(G, G) :-
nonvar(G), G = [_|_], !.

View File

@@ -1,7 +1,7 @@
:- module(dif, [dif/2]).
:- use_module(library(atts)).
:- use_module(library(lists), [append/3]).
:- use_module('src/prolog/lib/atts.pl').
:- use_module('src/prolog/lib/lists.pl', [append/3]).
:- attribute dif/1.

View File

@@ -1,6 +1,6 @@
:- module(freeze, [freeze/2]).
:- use_module(library(atts)).
:- use_module('src/prolog/lib/atts.pl').
:- attribute frozen/1.

View File

@@ -52,7 +52,7 @@
ord_intersection/2 % +PowerSet, -Intersection
]).
:- use_module(library(lists)).
:- use_module('src/prolog/lib/lists.pl').
/** <module> Ordered set manipulation
Ordered sets are lists with unique elements sorted to the standard order

View File

@@ -2,7 +2,7 @@
memberd_t/3, tfilter/3, tmember/2, tmember_t/3,
tpartition/4]).
:- use_module(library(dif)).
:- use_module('src/prolog/lib/dif.pl').
if_(If_1, Then_0, Else_0) :-
call(If_1, T),

View File

@@ -1,6 +1,6 @@
:- module(terms, [numbervars/3]).
:- use_module(library(error)).
:- use_module('src/prolog/lib/error.pl').
numbervars(Term, N0, N) :-
catch(internal_numbervars(Term, N0, N), error(E,Ctx),

View File

@@ -14,8 +14,10 @@ use prolog::machine::toplevel::*;
use std::cell::Cell;
use std::collections::{HashMap, HashSet, VecDeque};
use std::fs::File;
use std::io::Read;
use std::mem;
use std::path::PathBuf;
#[allow(dead_code)]
fn print_code(code: &Code) {
@@ -41,6 +43,59 @@ fn print_code(code: &Code) {
}
}
fn fix_filename(atom_tbl: TabledData<Atom>, filename: &str) -> Result<PathBuf, SessionError>
{
let mut path = PathBuf::from(filename);
if !path.is_file() {
if path.extension().is_none() {
path.set_extension("pl");
}
if !path.is_file() {
let filename = clause_name!(path.to_string_lossy().to_string(), atom_tbl);
return Err(SessionError::InvalidFileName(filename));
}
}
Ok(path)
}
fn load_module_from_file(wam: &mut Machine, filename: &str) -> Result<ClauseName, SessionError>
{
let path = fix_filename(wam.indices.atom_tbl.clone(), filename)?;
let file_handle = File::open(&path).or_else(|_| {
let filename = clause_name!(path.to_string_lossy().to_string(), wam.indices.atom_tbl);
Err(SessionError::InvalidFileName(filename))
})?;
let file_src = parsing_stream(file_handle);
// follow the operation of compile_user_module, but before
// compiling, check that a module is declared in the file. if not,
// throw an exception.
let mut indices = default_index_store!(wam.indices.atom_tbl.clone());
setup_indices(wam, clause_name!("builtins"), &mut indices)?;
let mut compiler = ListingCompiler::new(&wam.code_repo);
let results = compiler.gather_items(wam, file_src, &mut indices)?;
let module_name = if let Some(ref module) = &compiler.module {
module.module_decl.name.clone()
} else {
let module_name = path.to_string_lossy().to_string();
let module_name = clause_name!(module_name, wam.indices.atom_tbl);
return Err(SessionError::NoModuleDeclaration(module_name));
};
match compile_work_impl(&mut compiler, wam, indices, results) {
EvalSession::Error(e) => return Err(e),
_ => Ok(module_name)
}
}
pub type PredicateCompileQueue = (Predicate, VecDeque<TopLevel>);
// throw errors if declaration or query found.
@@ -96,6 +151,7 @@ impl CodeRepo {
-> Result<(), ParserError>
{
let key = (hook.name(), hook.arity());
match self.term_dir.get(&key) {
Some(preds) => {
let mut cg = CodeGenerator::<DebrayAllocator>::new(false, flags);
@@ -131,10 +187,9 @@ fn compile_decl(wam: &mut Machine, compiler: &mut ListingCompiler, decl: Declara
{
let flags = wam.machine_flags();
let mut indices = default_index_store!(wam.indices.atom_tbl.clone());
let wam_indices = &mut wam.indices;
compiler.process_decl(decl, &mut wam.code_repo, wam_indices, &mut indices, flags)?;
compiler.process_decl(decl, wam, &mut indices, flags)?;
Ok(indices)
}
@@ -407,19 +462,18 @@ impl ListingCompiler {
}
}
fn use_module(&mut self, submodule: ClauseName, code_repo: &mut CodeRepo,
flags: MachineFlags, wam_indices: &mut IndexStore,
indices: &mut IndexStore)
fn use_module(&mut self, submodule: ClauseName, code_repo: &mut CodeRepo, flags: MachineFlags,
wam_indices: &mut IndexStore, indices: &mut IndexStore)
-> Result<(), SessionError>
{
let mod_name = self.get_module_name();
let module_name = self.get_module_name();
if let Some(mut submodule) = wam_indices.take_module(submodule) {
unwind_protect!(indices.use_module(code_repo, flags, &submodule),
wam_indices.insert_module(submodule));
if let &mut Some(ref mut module) = &mut self.module {
module.remove_module(mod_name, &submodule);
module.remove_module(module_name, &submodule);
unwind_protect!(module.use_module(code_repo, flags, &submodule),
wam_indices.insert_module(submodule));
} else {
@@ -438,14 +492,14 @@ impl ListingCompiler {
wam_indices: &mut IndexStore, indices: &mut IndexStore)
-> Result<(), SessionError>
{
let mod_name = self.get_module_name();
let module_name = self.get_module_name();
if let Some(mut submodule) = wam_indices.take_module(submodule) {
unwind_protect!(indices.use_qualified_module(code_repo, flags, &submodule, exports),
wam_indices.insert_module(submodule));
if let &mut Some(ref mut module) = &mut self.module {
module.remove_module(mod_name, &submodule);
module.remove_module(module_name, &submodule);
unwind_protect!(module.use_qualified_module(code_repo, flags, &submodule, exports),
wam_indices.insert_module(submodule));
} else {
@@ -536,19 +590,20 @@ impl ListingCompiler {
(len, queue_len)
}
fn process_decl(&mut self, decl: Declaration, code_repo: &mut CodeRepo,
wam_indices: &mut IndexStore, indices: &mut IndexStore,
fn process_decl(&mut self, decl: Declaration, wam: &mut Machine, indices: &mut IndexStore,
flags: MachineFlags)
-> Result<(), SessionError>
{
match decl {
Declaration::EndOfFile =>
return Ok(()),
Declaration::Hook(hook, clause, queue) => {
let key = (hook.name(), hook.arity());
let (len, queue_len) = self.add_term_dir_terms(hook, code_repo, key.clone(),
clause, queue);
let (len, queue_len) = self.add_term_dir_terms(hook, &mut wam.code_repo,
key.clone(), clause, queue);
let result = code_repo.compile_hook(hook, flags).map_err(SessionError::from);
code_repo.truncate_terms(key, len, queue_len);
let result = wam.code_repo.compile_hook(hook, flags).map_err(SessionError::from);
wam.code_repo.truncate_terms(key, len, queue_len);
result
},
@@ -556,15 +611,17 @@ impl ListingCompiler {
Ok(self.add_non_counted_bt_flag(name, arity)),
Declaration::Op(op_decl) => {
let spec = get_desc(op_decl.name(), composite_op!(self.module.is_some(),
&wam_indices.op_dir,
&wam.indices.op_dir,
&mut indices.op_dir));
op_decl.submit(self.get_module_name(), spec, &mut indices.op_dir)
},
Declaration::UseModule(name) =>
self.use_module(name, code_repo, flags, wam_indices, indices),
Declaration::UseQualifiedModule(name, exports) =>
self.use_qualified_module(name, code_repo, flags, &exports, wam_indices, indices),
Declaration::UseModule(ModuleSource::Library(name)) =>
self.use_module(name, &mut wam.code_repo, flags, &mut wam.indices,
indices),
Declaration::UseQualifiedModule(ModuleSource::Library(name), exports) =>
self.use_qualified_module(name, &mut wam.code_repo, flags, &exports,
&mut wam.indices, indices),
Declaration::Module(module_decl) =>
if self.module.is_none() {
let module_name = module_decl.name.clone();
@@ -574,6 +631,15 @@ impl ListingCompiler {
} else {
Err(SessionError::from(ParserError::InvalidModuleDecl))
},
Declaration::UseModule(ModuleSource::File(filename)) => {
let name = load_module_from_file(wam, filename.as_str())?;
self.use_module(name, &mut wam.code_repo, flags, &mut wam.indices, indices)
},
Declaration::UseQualifiedModule(ModuleSource::File(filename), exports) => {
let name = load_module_from_file(wam, filename.as_str())?;
self.use_qualified_module(name, &mut wam.code_repo, flags, &exports,
&mut wam.indices, indices)
},
Declaration::Dynamic(..) => Ok(())
}
}
@@ -583,6 +649,8 @@ impl ListingCompiler {
indices: &mut IndexStore, flags: MachineFlags)
-> Result<(), SessionError>
{
let mut update_expansion_lengths = false;
match &decl {
&Declaration::Dynamic(ref name, arity) => {
worker.dynamic_clause_map.entry((name.clone(), arity)).or_insert(vec![]);
@@ -591,11 +659,19 @@ impl ListingCompiler {
worker.term_stream.incr_expansion_lens(hook.user_scope(), 1, queue.len()),
&Declaration::Hook(hook, _, ref queue) if !hook.has_module_scope() =>
worker.term_stream.incr_expansion_lens(hook, 1, queue.len()),
&Declaration::UseModule(ModuleSource::File(_))
| &Declaration::UseQualifiedModule(ModuleSource::File(_), _) =>
update_expansion_lengths = true,
_ => {}
};
self.process_decl(decl, &mut worker.term_stream.code_repo,
&mut worker.term_stream.indices, indices, flags)
let result = self.process_decl(decl, &mut worker.term_stream.wam, indices, flags);
if update_expansion_lengths {
worker.term_stream.update_expansion_lens();
}
result
}
pub(crate)
@@ -605,9 +681,7 @@ impl ListingCompiler {
{
let flags = wam.machine_flags();
let atom_tbl = indices.atom_tbl.clone();
let mut worker = TopLevelBatchWorker::new(&mut src, atom_tbl.clone(), flags,
&mut wam.indices, &mut wam.policies,
&mut wam.code_repo);
let mut worker = TopLevelBatchWorker::new(&mut src, atom_tbl.clone(), flags, wam);
let mut toplevel_results = vec![];
let mut toplevel_indices = default_index_store!(atom_tbl.clone());
@@ -623,6 +697,8 @@ impl ListingCompiler {
if let &Some(ref module) = &self.module {
worker.term_stream.set_atom_tbl(module.atom_tbl.clone());
}
} else if decl.is_end_of_file() {
break;
} else {
self.process_and_commit_decl(decl, &mut worker, indices, flags)?;
}
@@ -652,12 +728,10 @@ impl ListingCompiler {
}
}
fn compile_work<R: Read>(compiler: &mut ListingCompiler, wam: &mut Machine,
src: ParsingStream<R>, mut indices: IndexStore)
-> EvalSession
fn compile_work_impl(compiler: &mut ListingCompiler, wam: &mut Machine,
mut indices: IndexStore, mut results: GatherResult)
-> EvalSession
{
let mut results = try_eval_session!(compiler.gather_items(wam, src, &mut indices));
let module_code = try_eval_session!(compiler.generate_code(results.worker_results, wam,
&mut indices.code_dir, 0));
let toplvl_code = try_eval_session!(compiler.generate_code(results.toplevel_results, wam,
@@ -693,6 +767,14 @@ fn compile_work<R: Read>(compiler: &mut ListingCompiler, wam: &mut Machine,
EvalSession::EntrySuccess
}
fn compile_work<R: Read>(compiler: &mut ListingCompiler, wam: &mut Machine,
src: ParsingStream<R>, mut indices: IndexStore)
-> EvalSession
{
let results = try_eval_session!(compiler.gather_items(wam, src, &mut indices));
compile_work_impl(compiler, wam, indices, results)
}
/* This is a truncated version of compile_user_module, used for
compiling code composing special forms, ie. the code that calls
M:verify_attributes on attributed variables. */

View File

@@ -52,22 +52,32 @@ impl MachineError {
MachineError { stub, from: ErrorProvenance::Constructed }
}
pub(super) fn existence_error(h: usize, name: ClauseName, arity: usize) -> Self {
let mut stub = functor!("existence_error", 2, [heap_atom!("procedure"), heap_str!(3 + h)]);
stub.append(&mut Self::functor_stub(name, arity));
pub(super) fn existence_error(h: usize, err: ExistenceError) -> Self
{
match err {
ExistenceError::Procedure(name, arity) => {
let mut stub = functor!("existence_error", 2, [heap_atom!("procedure"), heap_str!(3 + h)]);
stub.append(&mut Self::functor_stub(name, arity));
MachineError { stub, from: ErrorProvenance::Constructed }
MachineError { stub, from: ErrorProvenance::Constructed }
},
ExistenceError::Module(name) => {
let name = HeapCellValue::Addr(Addr::Con(Constant::Atom(name, None)));
let mut stub = functor!("existence_error", 2, [heap_atom!("module"), name]);
MachineError { stub, from: ErrorProvenance::Constructed }
}
}
}
// so far, this function is only called wrt dynamic database
// transactions. their inapplicable error cases have been left
// unhandled.
pub(super) fn session_error(h: usize, err: SessionError) -> Self {
match err {
SessionError::ParserError(err) => Self::syntax_error(h, err),
SessionError::CannotOverwriteBuiltIn(pred_str)
| SessionError::CannotOverwriteImport(pred_str) =>
Self::permission_error(PermissionError::Modify, "private_procedure", pred_str),
SessionError::InvalidFileName(filename) =>
Self::existence_error(h, ExistenceError::Module(filename)),
SessionError::ModuleDoesNotContainExport =>
Self::permission_error(PermissionError::Access,
"private_procedure",
@@ -76,6 +86,8 @@ impl MachineError {
Self::permission_error(PermissionError::Access,
"private_procedure",
clause_name!("module_does_not_exist")),
SessionError::NoModuleDeclaration(name) =>
Self::existence_error(h, ExistenceError::Module(name)),
SessionError::OpIsInfixAndPostFix(op) =>
Self::permission_error(PermissionError::Create,
"operator",
@@ -396,12 +408,19 @@ impl MachineState {
}
}
pub enum ExistenceError {
Module(ClauseName),
Procedure(ClauseName, usize)
}
pub enum SessionError {
CannotOverwriteBuiltIn(ClauseName),
CannotOverwriteImport(ClauseName),
ModuleDoesNotContainExport,
InvalidFileName(ClauseName),
ModuleDoesNotContainExport,
ModuleNotFound,
NamelessEntry,
NoModuleDeclaration(ClauseName),
OpIsInfixAndPostFix(ClauseName),
ParserError(ParserError),
UserPrompt

View File

@@ -376,9 +376,9 @@ fn try_in_situ(machine_st: &mut MachineState, name: ClauseName, arity: usize,
} else {
let stub = MachineError::functor_stub(name.clone(), arity);
let h = machine_st.heap.h;
let key = ExistenceError::Procedure(name, arity);
Err(machine_st.error_form(MachineError::existence_error(h, name, arity),
stub))
Err(machine_st.error_form(MachineError::existence_error(h, key), stub))
}
}
@@ -797,8 +797,9 @@ pub(crate) trait CallPolicy: Any {
} else {
let h = machine_st.heap.h;
let stub = MachineError::functor_stub(clause_name!("call"), arity + 1);
let key = ExistenceError::Procedure(name, arity);
return Err(machine_st.error_form(MachineError::existence_error(h, name, arity),
return Err(machine_st.error_form(MachineError::existence_error(h, key),
stub));
}
},

View File

@@ -152,21 +152,8 @@ impl SubModuleUser for IndexStore {
}
static BUILTINS: &str = include_str!("../lib/builtins.pl");
static NON_ISO: &str = include_str!("../lib/non_iso.pl");
static LISTS: &str = include_str!("../lib/lists.pl");
static QUEUES: &str = include_str!("../lib/queues.pl");
static ERROR: &str = include_str!("../lib/error.pl");
static BETWEEN: &str = include_str!("../lib/between.pl");
static TERMS: &str = include_str!("../lib/terms.pl");
static DCGS: &str = include_str!("../lib/dcgs.pl");
static ATTS: &str = include_str!("../lib/atts.pl");
static DIF: &str = include_str!("../lib/dif.pl");
static FREEZE: &str = include_str!("../lib/freeze.pl");
static REIF: &str = include_str!("../lib/reif.pl");
static ASSOC: &str = include_str!("../lib/assoc.pl");
static ORDSETS: &str = include_str!("../lib/ordsets.pl");
static TOPLEVEL: &str = include_str!("../toplevel.pl");
static ERROR: &str = include_str!("../lib/error.pl");
impl Machine {
fn compile_special_forms(&mut self) {
@@ -192,22 +179,6 @@ impl Machine {
compile_user_module(self, parsing_stream(TOPLEVEL.as_bytes()));
}
fn compile_libraries(&mut self) {
compile_user_module(self, parsing_stream(NON_ISO.as_bytes()));
compile_user_module(self, parsing_stream(LISTS.as_bytes()));
compile_user_module(self, parsing_stream(QUEUES.as_bytes()));
compile_user_module(self, parsing_stream(ERROR.as_bytes()));
compile_user_module(self, parsing_stream(BETWEEN.as_bytes()));
compile_user_module(self, parsing_stream(TERMS.as_bytes()));
compile_user_module(self, parsing_stream(DCGS.as_bytes()));
compile_user_module(self, parsing_stream(ATTS.as_bytes()));
compile_user_module(self, parsing_stream(ORDSETS.as_bytes()));
compile_user_module(self, parsing_stream(DIF.as_bytes()));
compile_user_module(self, parsing_stream(FREEZE.as_bytes()));
compile_user_module(self, parsing_stream(REIF.as_bytes()));
compile_user_module(self, parsing_stream(ASSOC.as_bytes()));
}
#[cfg(test)]
pub fn reset(&mut self) {
self.prolog_stream = readline::input_stream();
@@ -235,10 +206,11 @@ impl Machine {
compile_listing(&mut wam, parsing_stream(BUILTINS.as_bytes()),
default_index_store!(atom_tbl.clone()));
wam.compile_libraries();
wam.compile_special_forms();
wam.compile_top_level();
compile_user_module(&mut wam, parsing_stream(ERROR.as_bytes()));
wam
}
@@ -344,20 +316,13 @@ impl Machine {
#[cfg(feature = "readline_rs_compat")]
readline::set_line_mode(readline::LineMode::Multi);
let src = match readline::read_batch("") {
Ok(src) => src,
Err(e) => {
self.throw_session_error(e, (clause_name!("repl"), 0));
return;
}
};
let src = readline::input_stream();
#[cfg(feature = "readline_rs_compat")]
readline::set_line_mode(readline::LineMode::Single);
match compile_user_module(self, parsing_stream(&src[0 ..])) {
EvalSession::Error(e) =>
self.throw_session_error(e, (clause_name!("repl"), 0)),
match compile_user_module(self, src) {
EvalSession::Error(e) => self.throw_session_error(e, (clause_name!("repl"), 0)),
_ => {}
};
},
@@ -387,7 +352,7 @@ impl Machine {
self.machine_st.heap_locs = var_dict;
let term_output = self.machine_st.print_query(term, &self.indices.op_dir);
term_output.result()
},
Err(err_stub) => {
@@ -439,7 +404,7 @@ impl Machine {
if !(self.machine_st.b > 0) {
if bindings.is_empty() {
let space = if requires_space(&attr_goals, ".") { " " } else { "" };
if !attr_goals.is_empty() {
println!("{}{}.", attr_goals, space);
} else {
@@ -511,7 +476,7 @@ impl Machine {
} else {
if requires_space(&bindings, ".") { " " } else { "" }
};
write!(raw_stdout, "{}.\r\n", space).unwrap();
}
@@ -647,9 +612,8 @@ impl Machine {
}
}
impl MachineState {
fn record_var_places(&mut self, chunk_num: usize, alloc_locs: &AllocVarDict)
fn record_var_places(&mut self, chunk_num: usize, alloc_locs: &AllocVarDict)
{
for (var, var_data) in alloc_locs {
match var_data {
@@ -673,16 +637,16 @@ impl MachineState {
}
}
}
fn print_query(&mut self, addr: Addr, op_dir: &OpDir) -> PrinterOutputter
{
let flags = self.flags;
let mut output = {
self.flags = MachineFlags { double_quotes: DoubleQuotes::Atom };
let output = PrinterOutputter::new();
let mut printer = HCPrinter::from_heap_locs(&self, op_dir, output);
let mut printer = HCPrinter::from_heap_locs(&self, op_dir, output);
printer.quoted = true;
printer.numbervars = false;
@@ -774,8 +738,8 @@ impl MachineState {
return false,
CodePtr::DynamicTransaction(..) => {
// prevent use of dynamic transactions from
// succeeding in expansions. this will be toggled
// back to true later.
// succeeding in expansions. self.fail will be toggled
// back to false later.
self.fail = true;
return false;
},

View File

@@ -117,7 +117,7 @@ pub trait SubModuleUser
};
}
}
// returns true on successful import.
fn import_decl(&mut self, name: ClauseName, arity: usize, submodule: &Module) -> bool
{
@@ -155,8 +155,7 @@ pub trait SubModuleUser
fn use_qualified_module(&mut self, &mut CodeRepo, MachineFlags, &Module, &Vec<PredicateKey>)
-> Result<(), SessionError>;
fn use_module(&mut self, &mut CodeRepo, MachineFlags, &Module)
-> Result<(), SessionError>;
fn use_module(&mut self, &mut CodeRepo, MachineFlags, &Module) -> Result<(), SessionError>;
}
pub fn use_qualified_module<User>(user: &mut User, submodule: &Module, exports: &Vec<PredicateKey>)

View File

@@ -282,7 +282,7 @@ impl MachineState {
self.fail = true;
return;
}
if let Some(r) = a2.as_var() {
let spec = get_clause_spec(name.clone(), *arity,
composite_op!(&indices.op_dir));
@@ -308,7 +308,7 @@ impl MachineState {
self.fail = true;
return;
}
if let Some(r) = a2.as_var() {
let spec = get_clause_spec(name.clone(), *arity,
composite_op!(&indices.op_dir));
@@ -1589,6 +1589,10 @@ impl MachineState {
self.unify(list_offset, a2);
},
Err(err) => {
if let ParserError::UnexpectedEOF = err {
std::process::exit(0);
}
// reset the input stream after an input failure.
*current_input_stream = readline::input_stream();

View File

@@ -71,9 +71,7 @@ fn extract_from_list(head: Box<Term>, tail: Box<Term>)
pub struct TermStream<'a, R: Read> {
stack: Vec<Term>,
pub(crate) indices: &'a mut IndexStore,
policies: &'a mut MachinePolicies,
pub(crate) code_repo: &'a mut CodeRepo,
pub(crate) wam: &'a mut Machine,
parser: Parser<'a, R>,
in_module: bool,
pub(crate) flags: MachineFlags,
@@ -104,25 +102,21 @@ impl ExpansionAdditionResult {
impl<'a, R: Read> Drop for TermStream<'a, R> {
fn drop(&mut self) {
self.indices.in_situ_code_dir.clear();
self.code_repo.in_situ_code.clear();
self.wam.indices.in_situ_code_dir.clear();
self.wam.code_repo.in_situ_code.clear();
discard_result!(self.rollback_expansion_code());
}
}
impl<'a, R: Read> TermStream<'a, R> {
pub fn new(src: &'a mut ParsingStream<R>, atom_tbl: TabledData<Atom>, flags: MachineFlags,
indices: &'a mut IndexStore, policies: &'a mut MachinePolicies,
code_repo: &'a mut CodeRepo)
pub fn new(src: &'a mut ParsingStream<R>, atom_tbl: TabledData<Atom>, flags: MachineFlags, wam: &'a mut Machine)
-> Self
{
TermStream {
stack: Vec::new(),
term_expansion_lens: code_repo.term_dir_entry_len((clause_name!("term_expansion"), 2)),
goal_expansion_lens: code_repo.term_dir_entry_len((clause_name!("goal_expansion"), 2)),
code_repo,
indices,
policies,
term_expansion_lens: wam.code_repo.term_dir_entry_len((clause_name!("term_expansion"), 2)),
goal_expansion_lens: wam.code_repo.term_dir_entry_len((clause_name!("goal_expansion"), 2)),
wam,
parser: Parser::new(src, atom_tbl, flags),
in_module: false,
flags
@@ -149,6 +143,22 @@ impl<'a, R: Read> TermStream<'a, R> {
}
}
#[inline]
pub fn update_expansion_lens(&mut self) {
let te_key = (clause_name!("term_expansion"), 2);
let ge_key = (clause_name!("goal_expansion"), 2);
let (tes_len, tes_q_len) = self.wam.code_repo.term_dir_entry_len(te_key);
self.term_expansion_lens.0 = tes_len;
self.term_expansion_lens.1 = tes_q_len;
let (ges_len, ges_q_len) = self.wam.code_repo.term_dir_entry_len(ge_key);
self.goal_expansion_lens.0 = ges_len;
self.goal_expansion_lens.1 = ges_q_len;
}
#[inline]
pub fn set_atom_tbl(&mut self, atom_tbl: TabledData<Atom>) {
self.parser.set_atom_tbl(atom_tbl);
@@ -159,7 +169,8 @@ impl<'a, R: Read> TermStream<'a, R> {
Ok(self.stack.is_empty() && self.parser.eof()?)
}
pub fn rollback_expansion_code(&mut self) -> Result<ExpansionAdditionResult, ParserError> {
pub fn rollback_expansion_code(&mut self) -> Result<ExpansionAdditionResult, ParserError>
{
let te_len = self.term_expansion_lens.0;
let te_queue_len = self.term_expansion_lens.1;
@@ -167,14 +178,14 @@ impl<'a, R: Read> TermStream<'a, R> {
let ge_queue_len = self.goal_expansion_lens.1;
let term_expansion_additions =
self.code_repo.truncate_terms((clause_name!("term_expansion"), 2),
te_len, te_queue_len);
self.wam.code_repo.truncate_terms((clause_name!("term_expansion"), 2),
te_len, te_queue_len);
let goal_expansion_additions =
self.code_repo.truncate_terms((clause_name!("goal_expansion"), 2),
ge_len, ge_queue_len);
self.wam.code_repo.truncate_terms((clause_name!("goal_expansion"), 2),
ge_len, ge_queue_len);
self.code_repo.compile_hook(CompileTimeHook::TermExpansion, self.flags)?;
self.code_repo.compile_hook(CompileTimeHook::GoalExpansion, self.flags)?;
self.wam.code_repo.compile_hook(CompileTimeHook::TermExpansion, self.flags)?;
self.wam.code_repo.compile_hook(CompileTimeHook::GoalExpansion, self.flags)?;
Ok(ExpansionAdditionResult {
term_expansion_additions,
@@ -200,7 +211,7 @@ impl<'a, R: Read> TermStream<'a, R> {
let mut stream = parsing_stream(term_string.trim().as_bytes());
let mut parser = Parser::new(&mut stream, self.parser.get_atom_tbl(), self.flags);
parser.read_term(composite_op!(self.in_module, &self.indices.op_dir, op_dir))
parser.read_term(composite_op!(self.in_module, &self.wam.indices.op_dir, op_dir))
}
pub fn read_term(&mut self, op_dir: &OpDir) -> Result<Term, ParserError>
@@ -209,8 +220,7 @@ impl<'a, R: Read> TermStream<'a, R> {
loop {
while let Some(term) = self.stack.pop() {
match machine_st.try_expand_term(self.indices, self.policies, self.code_repo,
&term, CompileTimeHook::TermExpansion)
match machine_st.try_expand_term(self.wam, &term, CompileTimeHook::TermExpansion)
{
Some(term_string) => {
let term = self.parse_expansion_output(term_string.as_str(), op_dir)?;
@@ -224,7 +234,7 @@ impl<'a, R: Read> TermStream<'a, R> {
}
self.parser.reset();
let term = self.parser.read_term(composite_op!(self.in_module, &self.indices.op_dir,
let term = self.parser.read_term(composite_op!(self.in_module, &self.wam.indices.op_dir,
op_dir))?;
self.stack.push(term);
}
@@ -242,8 +252,7 @@ impl<'a, R: Read> TermStream<'a, R> {
let comma_term = *terms.pop().unwrap();
unfold_by_str(comma_term, ",")
},
("?-", 1) =>
unfold_by_str(*terms.pop().unwrap(), ","),
("?-", 1) => unfold_by_str(*terms.pop().unwrap(), ","),
_ => return Ok(Term::Clause(cell, name, terms, arity))
};
@@ -266,10 +275,10 @@ impl<'a, R: Read> TermStream<'a, R> {
let mut results = vec![];
while let Some(term) = terms.pop_front() {
match machine_st.try_expand_term(self.indices, self.policies, self.code_repo,
&term, CompileTimeHook::GoalExpansion)
match machine_st.try_expand_term(self.wam, &term, CompileTimeHook::GoalExpansion)
{
Some(term_string) => {
println!("trying to goal expand {}", term_string);
let term = self.parse_expansion_output(term_string.as_str(), op_dir)?;
match term {
@@ -318,8 +327,7 @@ impl MachineState {
output
}
fn try_expand_term(&mut self, indices: &mut IndexStore, policies: &mut MachinePolicies,
code_repo: &mut CodeRepo, term: &Term, hook: CompileTimeHook)
fn try_expand_term(&mut self, wam: &mut Machine, term: &Term, hook: CompileTimeHook)
-> Option<String>
{
let term_write_result = write_term_to_heap(term, self);
@@ -331,17 +339,17 @@ impl MachineState {
let code = vec![call_clause!(ClauseType::Hook(hook), 2, 0, true)];
code_repo.cached_query = code;
self.query_stepper(indices, policies, code_repo, &mut readline::input_stream());
wam.code_repo.cached_query = code;
self.query_stepper(&mut wam.indices, &mut wam.policies, &mut wam.code_repo, &mut readline::input_stream());
if self.fail {
self.reset();
None
} else {
let TermWriteResult { var_dict, .. } = term_write_result;
self.heap_locs = var_dict;
let output = self.print_with_locs(Addr::HeapCell(h), &indices.op_dir);
let output = self.print_with_locs(Addr::HeapCell(h), &wam.indices.op_dir);
self.reset();
Some(output.result())

View File

@@ -4,7 +4,6 @@ use prolog_parser::tabled_rc::*;
use prolog::forms::*;
use prolog::iterators::*;
use prolog::machine::*;
use prolog::machine::code_repo::*;
use prolog::machine::machine_errors::*;
use prolog::machine::machine_indices::*;
use prolog::machine::machine_state::MachineState;
@@ -193,31 +192,37 @@ fn setup_module_decl(mut terms: Vec<Box<Term>>) -> Result<ModuleDecl, ParserErro
}
}
fn setup_use_module_decl(mut terms: Vec<Box<Term>>) -> Result<ClauseName, ParserError>
fn setup_use_module_decl(mut terms: Vec<Box<Term>>) -> Result<ModuleSource, ParserError>
{
match *terms.pop().unwrap() {
Term::Clause(_, ref name, ref mut terms, None)
if name.as_str() == "library" && terms.len() == 1 => {
terms.pop().unwrap().to_constant()
.and_then(|c| c.to_atom())
.ok_or(ParserError::InvalidUseModuleDecl)
.and_then(|c| c.to_atom())
.map(|c| ModuleSource::Library(c))
.ok_or(ParserError::InvalidUseModuleDecl)
},
Term::Constant(_, Constant::Atom(ref name, _)) =>
Ok(ModuleSource::File(name.clone())),
_ => Err(ParserError::InvalidUseModuleDecl)
}
}
type UseModuleExport = (ClauseName, Vec<PredicateKey>);
type UseModuleExport = (ModuleSource, Vec<PredicateKey>);
fn setup_qualified_import(mut terms: Vec<Box<Term>>) -> Result<UseModuleExport, ParserError>
{
let mut export_list = *terms.pop().unwrap();
let name = match *terms.pop().unwrap() {
let module_src = match *terms.pop().unwrap() {
Term::Clause(_, ref name, ref mut terms, None)
if name.as_str() == "library" && terms.len() == 1 => {
terms.pop().unwrap().to_constant()
.and_then(|c| c.to_atom())
.map(|c| ModuleSource::Library(c))
.ok_or(ParserError::InvalidUseModuleDecl)
},
Term::Constant(_, Constant::Atom(ref name, _)) =>
Ok(ModuleSource::File(name.clone())),
_ => Err(ParserError::InvalidUseModuleDecl)
}?;
@@ -231,7 +236,7 @@ fn setup_qualified_import(mut terms: Vec<Box<Term>>) -> Result<UseModuleExport,
if export_list.to_constant() != Some(Constant::EmptyList) {
Err(ParserError::InvalidModuleDecl)
} else {
Ok((name, exports))
Ok((module_src, exports))
}
}
@@ -768,7 +773,7 @@ fn term_to_toplevel<R>(term_stream: &mut TermStream<R>, code_dir: &mut CodeDir,
where R: Read
{
let mut rel_worker = RelationWorker::new(flags);
let mut indices = composite_indices!(false, term_stream.indices, code_dir);
let mut indices = composite_indices!(false, &mut term_stream.wam.indices, code_dir);
let tl = rel_worker.try_term_to_tl(&mut indices, term, true)?;
@@ -781,18 +786,17 @@ fn stream_to_toplevel<R: Read>(mut buffer: ParsingStream<R>, wam: &mut Machine)
{
let flags = wam.machine_flags();
let mut term_stream = TermStream::new(&mut buffer, wam.indices.atom_tbl(),
wam.machine_flags(), &mut wam.indices,
&mut wam.policies, &mut wam.code_repo);
wam.machine_flags(), wam);
term_stream.add_to_top("?- ");
let term = term_stream.read_term(&OpDir::new())?;
let mut code_dir = CodeDir::new();
let mut code_dir = CodeDir::new();
let (tl, mut rel_worker) = term_to_toplevel(&mut term_stream, &mut code_dir, term, flags)?;
rel_worker.expand_queue_contents(&mut term_stream, &OpDir::new())?;
let mut indices = composite_indices!(false, term_stream.indices, &mut code_dir);
let mut indices = composite_indices!(false, &mut term_stream.wam.indices, &mut code_dir);
let queue = rel_worker.parse_queue(&mut indices)?;
Ok(deque_to_packet(tl, queue))
@@ -811,12 +815,10 @@ pub struct TopLevelBatchWorker<'a, R: Read> {
impl<'a, R: Read> TopLevelBatchWorker<'a, R> {
pub fn new(inner: &'a mut ParsingStream<R>, atom_tbl: TabledData<Atom>,
flags: MachineFlags, indices: &'a mut IndexStore,
policies: &'a mut MachinePolicies, code_repo: &'a mut CodeRepo)
flags: MachineFlags, wam: &'a mut Machine)
-> Self
{
let term_stream = TermStream::new(inner, atom_tbl, flags,
indices, policies, code_repo);
let term_stream = TermStream::new(inner, atom_tbl, flags, wam);
TopLevelBatchWorker { term_stream,
rel_worker: RelationWorker::new(flags),
@@ -830,7 +832,7 @@ impl<'a, R: Read> TopLevelBatchWorker<'a, R> {
{
let mut new_rel_worker = RelationWorker::new(self.rel_worker.flags);
let mut indices = composite_indices!(self.in_module, indices,
&self.term_stream.indices.code_dir);
&self.term_stream.wam.indices.code_dir);
Ok((new_rel_worker.try_term_to_tl(&mut indices, term, true)?, new_rel_worker))
}
@@ -841,15 +843,15 @@ impl<'a, R: Read> TopLevelBatchWorker<'a, R> {
self.rel_worker.expand_queue_contents(&mut self.term_stream, &indices.op_dir)?;
let mut indices = composite_indices!(self.in_module, indices,
&mut self.term_stream.indices.code_dir);
&mut self.term_stream.wam.indices.code_dir);
let queue = self.rel_worker.parse_queue(&mut indices)?;
let result = (append_preds(preds), queue);
let in_situ_code_dir = &mut self.term_stream.indices.in_situ_code_dir;
let in_situ_code_dir = &mut self.term_stream.wam.indices.in_situ_code_dir;
self.term_stream.code_repo.add_in_situ_result(&result, in_situ_code_dir,
self.term_stream.flags)?;
self.term_stream.wam.code_repo.add_in_situ_result(&result, in_situ_code_dir,
self.term_stream.flags)?;
Ok(self.results.push(result))
}
@@ -873,14 +875,17 @@ impl<'a, R: Read> TopLevelBatchWorker<'a, R> {
}
}
pub fn consume(&mut self, indices: &mut IndexStore)
-> Result<Option<Declaration>, SessionError>
pub fn consume(&mut self, indices: &mut IndexStore) -> Result<Option<Declaration>, SessionError>
{
let mut preds = vec![];
while !self.term_stream.eof()? {
let term = self.term_stream.read_term(&indices.op_dir)?;
let (tl, new_rel_worker) = self.try_term_to_tl(indices, term)?;
let (mut tl, new_rel_worker) = self.try_term_to_tl(indices, term)?;
if tl.is_end_of_file_atom() {
tl = TopLevel::Declaration(Declaration::EndOfFile);
}
// if is_consistent is false, preds is non-empty.
if !is_consistent(&tl, &preds) {

View File

@@ -136,13 +136,6 @@ pub mod readline
bind_keyseq_rl("\\C-d", bind_end_chord);
}
pub fn read_batch(prompt: &str) -> Result<Vec<u8>, ::SessionError> {
match readline_rl(prompt) {
Some(input) => Ok(Vec::from(input.as_bytes())),
None => Err(::SessionError::UserPrompt)
}
}
#[inline]
pub fn input_stream() -> ::PrologStream {
let reader: Box<Read> = Box::new(ReadlineStream::new(String::from("")));

View File

@@ -2,7 +2,7 @@ repl :-
catch(read_and_match, E, '$print_exception'(E)),
false. %% this is for GC, until we get actual GC.
repl :- repl.
read_and_match :-
write_term('?- ', [quoted(false)]),
read_term(Term, [variable_names(VarList)]),

View File

@@ -261,9 +261,13 @@ impl fmt::Display for SessionError {
write!(f, "cannot overwrite {}", msg),
&SessionError::CannotOverwriteImport(ref msg) =>
write!(f, "cannot overwrite import {}", msg),
&SessionError::InvalidFileName(ref filename) =>
write!(f, "filename {} is invalid", filename),
&SessionError::ModuleNotFound => write!(f, "module not found."),
&SessionError::ModuleDoesNotContainExport =>
write!(f, "module does not contain claimed export."),
&SessionError::NoModuleDeclaration(ref name) =>
write!(f, "file {}.pl lacks an expected module declaration.", name),
&SessionError::OpIsInfixAndPostFix(_) =>
write!(f, "cannot define an op to be both postfix and infix."),
&SessionError::NamelessEntry =>

View File

@@ -1366,11 +1366,11 @@ fn test_queries_on_modules()
{
let mut wam = Machine::new(readline::input_stream());
submit(&mut wam, ":- use_module(library(lists)).");
submit(&mut wam, ":- use_module('src/prolog/lib/lists.pl').");
submit_code(&mut wam, "
:- module(my_lists, [local_member/2, reverse/2]).
:- use_module(library(lists), [member/2]).
:- use_module('src/prolog/lib/lists.pl', [member/2]).
local_member(X, Xs) :- member(X, Xs).
@@ -1387,13 +1387,13 @@ reverse(Xs, Ys) :- lists:reverse(Xs, Ys).");
assert_prolog_success!(&mut wam, "catch(local_member(X, Xs), error(E, _), true).",
[["X = _1", "E = existence_error(procedure,local_member/2)", "Xs = _2"]]);
submit(&mut wam, ":- use_module(library(lists), [reverse/2]).");
submit(&mut wam, ":- use_module('src/prolog/lib/lists.pl', [reverse/2]).");
assert_prolog_success!(&mut wam, "catch(member(_, _), error(existence_error(procedure, P), _), true).",
[["P = member/2"]]);
assert_prolog_success!(&mut wam, "reverse(_, _).");
submit(&mut wam, ":- use_module(library(lists), []).");
submit(&mut wam, ":- use_module('src/prolog/lib/lists.pl', []).");
assert_prolog_success!(&mut wam, "catch(reverse(_, _), error(existence_error(procedure, P), _), true).",
[["P = reverse/2"]]);
@@ -1404,8 +1404,7 @@ fn test_queries_on_builtins()
{
let mut wam = Machine::new(readline::input_stream());
submit(&mut wam, ":- use_module(library(lists)).");
submit(&mut wam, ":- use_module(library(control)).");
submit(&mut wam, ":- use_module('src/prolog/lib/lists.pl').");
assert_prolog_failure!(&mut wam, "atom(X).");
assert_prolog_success!(&mut wam, "atom(a).");
@@ -1611,7 +1610,7 @@ fn test_queries_on_builtins()
assert_prolog_success!(&mut wam, "1.0 @=< 1.");
assert_prolog_success!(&mut wam, "1 @=< 1.0.");
submit(&mut wam, ":- use_module(library(non_iso)).");
submit(&mut wam, ":- use_module('src/prolog/lib/non_iso.pl').");
assert_prolog_success!(&mut wam, "variant(X, Y).");
assert_prolog_failure!(&mut wam, "variant(f(X), f(x)).");
@@ -1662,8 +1661,6 @@ fn test_queries_on_builtins()
assert_prolog_success!(&mut wam, "call(((G = 2 ; fail),B=3,!)).",
[["G = 2","B = 3"]]);
submit(&mut wam, ":- use_module(library(non_iso)).");
assert_prolog_success!(&mut wam, "call_with_inference_limit((setup_call_cleanup(S=1,(G=2;fail),writeq(S+G>B)),B=3,!),135,R).",
[["G = 2","B = 3","R = !","S = 1"]]);
assert_prolog_success!(&mut wam, "call_with_inference_limit((setup_call_cleanup(S=1,(G=2;fail),writeq(S+G>B)),B=3,!),10,R).",
@@ -1802,8 +1799,6 @@ fn test_queries_on_builtins()
assert_prolog_success!(&mut wam, "setof(X,Y^((X = 1 ; Y = 1) ; (X = 2,Y = 2)),S).",
[["S = [_126,1,2]","X = _0","Y = _5"]]);
submit(&mut wam, ":- use_module(library(non_iso)).");
assert_prolog_failure!(&mut wam, "forall(true,false).");
assert_prolog_success!(&mut wam, "forall(false,true).");
assert_prolog_success!(&mut wam, "catch(forall(_,true),error(instantiation_error,_),true).");
@@ -1971,7 +1966,7 @@ fn test_queries_on_setup_call_cleanup()
{
let mut wam = Machine::new(readline::input_stream());
submit(&mut wam, ":- use_module(library(non_iso)).");
submit(&mut wam, ":- use_module('src/prolog/lib/non_iso.pl').");
// Test examples from the ISO Prolog page for setup_call_catch.
assert_prolog_failure!(&mut wam, "setup_call_cleanup(false, _, _).");
@@ -2024,7 +2019,7 @@ fn test_queries_on_setup_call_cleanup()
// fails here.
assert_prolog_success!(&mut wam,
"setup_call_cleanup(true, (X=1;X=2), writeq(a)), setup_call_cleanup(true,(Y=1;Y=2),writeq(b)), !.",
"setup_call_cleanup(true, (X=1;X=2), writeq(a)), setup_call_cleanup(true,(Y=1;Y=2),writeq(b)), !.",
[["Y = 1", "X = 1"]]);
}
@@ -2033,7 +2028,7 @@ fn test_queries_on_call_with_inference_limit()
{
let mut wam = Machine::new(readline::input_stream());
submit(&mut wam, ":- use_module(library(non_iso)).");
submit(&mut wam, ":- use_module('src/prolog/lib/non_iso.pl').");
assert_prolog_success!(&mut wam, "call_with_inference_limit(throw(error), 0, R).",
[["R = inference_limit_exceeded"]]);
@@ -2155,7 +2150,7 @@ fn test_queries_on_dcgs()
{
let mut wam = Machine::new(readline::input_stream());
submit(&mut wam, ":- use_module(library(dcgs)).");
submit(&mut wam, ":- use_module('src/prolog/lib/dcgs.pl').");
// test case by YeGoblynQueene from hacker news.
submit_code(&mut wam,
@@ -2186,7 +2181,7 @@ fn test_queries_on_string_lists()
{
let mut wam = Machine::new(readline::input_stream());
submit(&mut wam, ":- use_module(library(non_iso)).");
submit(&mut wam, ":- use_module('src/prolog/lib/non_iso.pl').");
// double_quotes is chars by default.
assert_prolog_success!(&mut wam, "variant(\"\", []).");
@@ -2399,7 +2394,7 @@ fn test_queries_on_attributed_variables()
submit(&mut wam, "
:- module(my_mod, []).
:- use_module(library(atts)).
:- use_module('src/prolog/lib/atts.pl').
:- attribute dif/1, frozen/1.");
@@ -2441,8 +2436,7 @@ fn test_queries_on_attributed_variables()
put_atts(V, my_mod, -dif(A)), get_atts(V, my_mod, Ls).",
[["A = _114", "Ls = [frozen(b)]", "V = _29"]]);
submit(&mut wam, include_str!("./prolog/examples/minatotask.pl"));
submit(&mut wam, ":- use_module(library(zdd)).");
submit(&mut wam, ":- use_module('src/prolog/examples/minatotask.pl').");
assert_prolog_failure!(&mut wam, "ZDD = ( X -> b(true) ; ( Y -> b(true) ; b(false) ) ),
Vs = [X,Y],