[package]
name = "scryer-prolog"
-version = "0.8.86"
+version = "0.8.87"
repository = "https://github.com/mthom/scryer-prolog"
description = "A modern Prolog implementation written mostly in Rust."
?- 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:
```
?- 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.
\ No newline at end of file
mod prolog;
use prolog::machine::*;
-use prolog::machine::machine_errors::*;
use prolog::read::*;
#[cfg(test)]
&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)]
}
}
+#[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 {
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)]
del_max_assoc/4 % +Assoc0, ?Key, ?Value, ?Assoc
]).
-:- use_module(library(lists)).
+:- use_module('src/prolog/lib/lists.pl').
/** <module> Binary associations
'$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).
%% 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),
:- 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 = [_|_], !.
:- 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.
:- module(freeze, [freeze/2]).
-:- use_module(library(atts)).
+:- use_module('src/prolog/lib/atts.pl').
:- attribute frozen/1.
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
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),
:- 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),
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) {
}
}
+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.
-> Result<(), ParserError>
{
let key = (hook.name(), hook.arity());
+
match self.term_dir.get(&key) {
Some(preds) => {
let mut cg = CodeGenerator::<DebrayAllocator>::new(false, flags);
{
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)
}
}
}
- 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 {
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 {
(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
},
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();
} 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(())
}
}
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![]);
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)
{
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());
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)?;
}
}
}
-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,
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. */
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",
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",
}
}
+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
} 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))
}
}
} 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));
}
},
}
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) {
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();
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
}
#[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)),
_ => {}
};
},
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) => {
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 {
} else {
if requires_space(&bindings, ".") { " " } else { "" }
};
-
+
write!(raw_stdout, "{}.\r\n", space).unwrap();
}
}
}
-
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 {
}
}
}
-
+
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;
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;
},
};
}
}
-
+
// returns true on successful import.
fn import_decl(&mut self, name: ClauseName, arity: usize, submodule: &Module) -> bool
{
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>)
self.fail = true;
return;
}
-
+
if let Some(r) = a2.as_var() {
let spec = get_clause_spec(name.clone(), *arity,
composite_op!(&indices.op_dir));
self.fail = true;
return;
}
-
+
if let Some(r) = a2.as_var() {
let spec = get_clause_spec(name.clone(), *arity,
composite_op!(&indices.op_dir));
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();
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,
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
}
}
+ #[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);
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;
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,
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>
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)?;
}
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);
}
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))
};
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 {
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);
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())
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;
}
}
-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)
}?;
if export_list.to_constant() != Some(Constant::EmptyList) {
Err(ParserError::InvalidModuleDecl)
} else {
- Ok((name, exports))
+ Ok((module_src, exports))
}
}
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)?;
{
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))
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),
{
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))
}
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))
}
}
}
- 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) {
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("")));
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)]),
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 =>
{
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).
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"]]);
{
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).");
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)).");
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).",
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).");
{
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, _, _).");
// 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"]]);
}
{
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"]]);
{
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,
{
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(\"\", []).");
submit(&mut wam, "
:- module(my_mod, []).
-:- use_module(library(atts)).
+:- use_module('src/prolog/lib/atts.pl').
:- attribute dif/1, frozen/1.");
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],