From b61ed65208d191450fe6202934bba92922df5063 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Thu, 3 Oct 2019 22:13:34 -0600 Subject: [PATCH] fix goal expansion in disjunctions, add warnings for singleton variables --- Cargo.toml | 4 +- src/prolog/codegen.rs | 6 +- src/prolog/forms.rs | 136 ++++++++++++++++++++--- src/prolog/iterators.rs | 11 +- src/prolog/lib/dcgs.pl | 6 +- src/prolog/machine/compile.rs | 146 +++++++++++++++++-------- src/prolog/machine/dynamic_database.rs | 4 +- src/prolog/machine/mod.rs | 23 ++-- src/prolog/machine/term_expansion.rs | 12 +- src/prolog/machine/toplevel.rs | 112 ++++++++++++++----- src/tests.rs | 2 +- 11 files changed, 347 insertions(+), 115 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bd31faa2..8a31692a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "scryer-prolog" -version = "0.8.98" +version = "0.8.99" authors = ["Mark Thom "] build = "build.rs" repository = "https://github.com/mthom/scryer-prolog" @@ -18,7 +18,7 @@ lazy_static = "1.4.0" libc = "0.2.62" nix = "0.15.0" ordered-float = "0.5.0" -prolog_parser = "0.8.30" +prolog_parser = "0.8.31" ref_thread_local = "0.0.0" rug = "1.4.0" rustyline = "5.0.3" diff --git a/src/prolog/codegen.rs b/src/prolog/codegen.rs index 5d7526bc..d97c904f 100644 --- a/src/prolog/codegen.rs +++ b/src/prolog/codegen.rs @@ -20,7 +20,7 @@ use std::vec::Vec; pub struct CodeGenerator { flags: MachineFlags, marker: TermMarker, - var_count: IndexMap, usize>, + pub var_count: IndexMap, usize>, non_counted_bt: bool, } @@ -867,8 +867,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator { self.marker.reset(); let mut clause_code = match clause { - &PredicateClause::Fact(ref fact) => self.compile_fact(fact), - &PredicateClause::Rule(ref rule) => try!(self.compile_rule(rule)), + &PredicateClause::Fact(ref fact, ..) => self.compile_fact(fact), + &PredicateClause::Rule(ref rule, ..) => self.compile_rule(rule)?, }; if num_clauses > 1 { diff --git a/src/prolog/forms.rs b/src/prolog/forms.rs index 0f05587c..f6b9e25f 100644 --- a/src/prolog/forms.rs +++ b/src/prolog/forms.rs @@ -3,6 +3,7 @@ use prolog_parser::parser::OpDesc; use prolog_parser::tabled_rc::*; use prolog::clause_types::*; +use prolog::iterators::*; use prolog::machine::machine_errors::*; use prolog::machine::machine_indices::*; use prolog::ordered_float::OrderedFloat; @@ -23,41 +24,75 @@ pub type JumpStub = Vec; #[derive(Clone)] pub enum TopLevel { Declaration(Declaration), - Fact(Term), + Fact(Term, usize, usize), // Term, line_num, col_num Predicate(Predicate), Query(Vec), - Rule(Rule), + Rule(Rule, usize, usize), // Rule, line_num, col_num } impl TopLevel { pub fn name(&self) -> Option { match self { &TopLevel::Declaration(_) => None, - &TopLevel::Fact(ref term) => term.name(), + &TopLevel::Fact(ref term, ..) => term.name(), &TopLevel::Predicate(ref clauses) => clauses.0.first().and_then(|ref term| term.name()), &TopLevel::Query(_) => None, - &TopLevel::Rule(Rule { ref head, .. }) => Some(head.0.clone()), + &TopLevel::Rule(Rule { ref head, .. }, ..) => Some(head.0.clone()), } } pub fn arity(&self) -> usize { match self { &TopLevel::Declaration(_) => 0, - &TopLevel::Fact(ref term) => term.arity(), + &TopLevel::Fact(ref term, ..) => term.arity(), &TopLevel::Predicate(ref clauses) => clauses.0.first().map(|t| t.arity()).unwrap_or(0), &TopLevel::Query(_) => 0, - &TopLevel::Rule(Rule { ref head, .. }) => head.1.len(), + &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, _))) => { + &TopLevel::Fact(Term::Constant(_, Constant::Atom(ref name, _)), ..) => { return name.as_str() == "end_of_file" } _ => false, } } + + pub fn location(&self) -> Option<(usize, usize)> { + match self { + &TopLevel::Fact(_, line_num, col_num) | &TopLevel::Rule(_, line_num, col_num) => + Some((line_num, col_num)), + &TopLevel::Predicate(Predicate(ref clauses)) => + clauses.first().map(|clause| (clause.line_num(), clause.col_num())), + _ => + None + } + } + + pub fn var_count(&self) -> Option, usize>>> { + match self { + &TopLevel::Fact(ref term, ..) => { + let mut var_count = IndexMap::new(); + + for term_ref in breadth_first_iter(term, true) { + if let TermRef::Var(_, _, var) = term_ref { + let entry = var_count.entry(var).or_insert(0); + *entry += 1; + } + } + + Some(vec![var_count]) + } + &TopLevel::Rule(ref rule, ..) => + Some(vec![rule.var_count()]), + &TopLevel::Predicate(ref predicate) => + Some(predicate.var_count()), + _ => + None + } + } } #[derive(Clone, Copy)] @@ -110,6 +145,40 @@ pub struct Rule { pub clauses: Vec, } +impl Rule { + pub fn var_count(&self) -> IndexMap, usize> { + let mut var_count = IndexMap::new(); + + for root_term in &self.head.1 { + // true because the root term is iterable. + for term_ref in breadth_first_iter(root_term, true) { + if let TermRef::Var(_, _, var) = term_ref { + let entry = var_count.entry(var).or_insert(0); + *entry += 1; + } + } + } + + for term_ref in query_term_post_order_iter(&self.head.2) { + if let TermRef::Var(_, _, var) = term_ref { + let entry = var_count.entry(var).or_insert(0); + *entry += 1; + } + } + + for clause in &self.clauses { + for term_ref in query_term_post_order_iter(clause) { + if let TermRef::Var(_, _, var) = term_ref { + let entry = var_count.entry(var).or_insert(0); + *entry += 1; + } + } + } + + var_count + } +} + #[derive(Clone)] pub struct Predicate(pub Vec); @@ -130,35 +199,72 @@ impl Predicate { .first() .and_then(|clause| clause.name().map(|name| (name, clause.arity()))) } + + pub fn var_count(&self) -> Vec, usize>> { + self.0.iter().map(|clause| clause.var_count()).collect() + } } pub type CompiledResult = (Predicate, VecDeque); #[derive(Clone)] pub enum PredicateClause { - Fact(Term), - Rule(Rule), + Fact(Term, usize, usize), // Term, line number, column number. + Rule(Rule, usize, usize), // Term, line number, column number. } impl PredicateClause { pub fn first_arg(&self) -> Option<&Term> { match self { - &PredicateClause::Fact(ref term) => term.first_arg(), - &PredicateClause::Rule(ref rule) => rule.head.1.first().map(|bt| bt.as_ref()), + &PredicateClause::Fact(ref term, ..) => term.first_arg(), + &PredicateClause::Rule(ref rule, ..) => rule.head.1.first().map(|bt| bt.as_ref()), } } pub fn arity(&self) -> usize { match self { - &PredicateClause::Fact(ref term) => term.arity(), - &PredicateClause::Rule(ref rule) => rule.head.1.len(), + &PredicateClause::Fact(ref term, ..) => term.arity(), + &PredicateClause::Rule(ref rule, ..) => rule.head.1.len(), } } pub fn name(&self) -> Option { match self { - &PredicateClause::Fact(ref term) => term.name(), - &PredicateClause::Rule(ref rule) => Some(rule.head.0.clone()), + &PredicateClause::Fact(ref term, ..) => term.name(), + &PredicateClause::Rule(ref rule, ..) => Some(rule.head.0.clone()), + } + } + + pub fn line_num(&self) -> usize { + match self { + &PredicateClause::Fact(_, line_num, _) => line_num, + &PredicateClause::Rule(_, line_num, _) => line_num + } + } + + pub fn col_num(&self) -> usize { + match self { + &PredicateClause::Fact(_, _, col_num) => col_num, + &PredicateClause::Rule(_, _, col_num) => col_num + } + } + + pub fn var_count(&self) -> IndexMap, usize> { + match self { + &PredicateClause::Fact(ref term, ..) => { + let mut var_count = IndexMap::new(); + + for term_ref in breadth_first_iter(term, true) { + if let TermRef::Var(_, _, var) = term_ref { + let entry = var_count.entry(var).or_insert(0); + *entry += 1; + } + } + + var_count + } + &PredicateClause::Rule(ref rule, ..) => + rule.var_count() } } } diff --git a/src/prolog/iterators.rs b/src/prolog/iterators.rs index 96724665..ddc77613 100644 --- a/src/prolog/iterators.rs +++ b/src/prolog/iterators.rs @@ -23,10 +23,10 @@ impl<'a> TermRef<'a> { pub fn level(self) -> Level { match self { TermRef::AnonVar(lvl) - | TermRef::Cons(lvl, ..) - | TermRef::Constant(lvl, ..) - | TermRef::Var(lvl, ..) - | TermRef::Clause(lvl, ..) => lvl, + | TermRef::Cons(lvl, ..) + | TermRef::Constant(lvl, ..) + | TermRef::Var(lvl, ..) + | TermRef::Clause(lvl, ..) => lvl, } } } @@ -112,7 +112,8 @@ impl<'a> QueryIterator<'a> { state_stack: vec![], } } - &Term::Var(ref cell, ref var) => TermIterState::Var(Level::Root, cell, (*var).clone()), + &Term::Var(ref cell, ref var) => + TermIterState::Var(Level::Root, cell, (*var).clone()), }; QueryIterator { diff --git a/src/prolog/lib/dcgs.pl b/src/prolog/lib/dcgs.pl index 1805dc80..3dea6bb3 100644 --- a/src/prolog/lib/dcgs.pl +++ b/src/prolog/lib/dcgs.pl @@ -81,10 +81,12 @@ expand_body_term([Arg|Args], ModTerm, N0, N) :- append([Arg|Args], '$VAR'(N), ModArgs), ModTerm = ('$VAR'(N0) = ModArgs). expand_body_term((P -> Q), (PModTerm -> QModTerm), N0, N) :- - !, expand_body(P, PModTerm, N0, N1), + !, + expand_body(P, PModTerm, N0, N1), expand_body(Q, QModTerm, N1, N). expand_body_term((P ; Q), (PModTerm ; QModTerm), N0, N) :- - !, expand_body(P, PModTerm0, N0, N1), + !, + expand_body(P, PModTerm0, N0, N1), expand_body(Q, QModTerm0, N0, N2), ( N1 == N2 -> PModTerm = PModTerm0, QModTerm = QModTerm0, diff --git a/src/prolog/machine/compile.rs b/src/prolog/machine/compile.rs index 2554e256..b226cb48 100644 --- a/src/prolog/machine/compile.rs +++ b/src/prolog/machine/compile.rs @@ -56,16 +56,18 @@ fn fix_filename(atom_tbl: TabledData, filename: &str) -> Result(wam: &mut Machine, stream: ParsingStream) - -> Result, SessionError> -{ +fn load_module( + wam: &mut Machine, + stream: ParsingStream, + suppress_warnings: bool, +) -> Result, SessionError> { // 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 mut compiler = ListingCompiler::new(&wam.code_repo, suppress_warnings); let results = compiler.gather_items(wam, stream, &mut indices)?; let module_name = if let Some(ref module) = &compiler.module { @@ -81,9 +83,11 @@ fn load_module(wam: &mut Machine, stream: ParsingStream) } pub(super) -fn load_module_from_file(wam: &mut Machine, filename: &str) - -> Result, SessionError> -{ +fn load_module_from_file( + wam: &mut Machine, + filename: &str, + suppress_warnings: bool, +) -> Result, SessionError> { let path = fix_filename(wam.indices.atom_tbl.clone(), filename)?; let file_handle = File::open(&path).or_else(|_| { @@ -91,24 +95,51 @@ fn load_module_from_file(wam: &mut Machine, filename: &str) Err(SessionError::InvalidFileName(filename)) })?; - load_module(wam, parsing_stream(file_handle)) + load_module(wam, parsing_stream(file_handle), suppress_warnings) } pub type PredicateCompileQueue = (Predicate, VecDeque); // throw errors if declaration or query found. fn compile_relation( - tl: &TopLevel, - non_counted_bt: bool, - flags: MachineFlags, + cg: &mut CodeGenerator, + tl: &TopLevel ) -> Result { - let mut cg = CodeGenerator::::new(non_counted_bt, flags); - match tl { &TopLevel::Declaration(_) | &TopLevel::Query(_) => Err(ParserError::ExpectedRel), &TopLevel::Predicate(ref clauses) => cg.compile_predicate(&clauses.0), - &TopLevel::Fact(ref fact) => Ok(cg.compile_fact(fact)), - &TopLevel::Rule(ref rule) => cg.compile_rule(rule), + &TopLevel::Fact(ref fact, ..) => Ok(cg.compile_fact(fact)), + &TopLevel::Rule(ref rule, ..) => cg.compile_rule(rule), + } +} + +fn issue_singleton_warnings( + tl: &TopLevel, + module_name: ClauseName, +) { + if let Some((line_num, _)) = tl.location() { + if let Some(var_counts) = tl.var_count() { + for var_count in var_counts { + let mut singletons = vec![]; + + for (var, count) in var_count { + if count == 1 && !var.starts_with("_") && var.as_str() != "!" { + singletons.push(var); + } + } + + if let Some(last_var) = singletons.pop() { + print!("Warning: {}:{}: Singleton variables: [", + module_name, line_num); + + for var in singletons { + print!("{}, ", var); + } + + println!("{}]", last_var); + } + } + } } } @@ -139,7 +170,9 @@ pub fn compile_appendix( ) -> Result<(), ParserError> { for tl in queue.iter() { set_first_index(code); - code.append(&mut compile_relation(tl, non_counted_bt, flags)?); + let mut cg = CodeGenerator::::new(non_counted_bt, flags); + let decl_code = compile_relation(&mut cg, tl)?; + code.extend(decl_code.into_iter()); } Ok(()) @@ -209,7 +242,7 @@ pub fn compile_term(wam: &mut Machine, packet: TopLevelPacket) -> EvalSession { } } TopLevelPacket::Decl(TopLevel::Declaration(decl), _) => { - let mut compiler = ListingCompiler::new(&wam.code_repo); + let mut compiler = ListingCompiler::new(&wam.code_repo, false); let indices = try_eval_session!(compile_decl(wam, &mut compiler, decl)); try_eval_session!(wam.check_toplevel_code(&indices)); @@ -294,7 +327,7 @@ pub(super) fn compile_into_module( let mut indices = default_index_store!(wam.atom_tbl_of(&name)); try_eval_session!(setup_indices(wam, module_name.clone(), &mut indices)); - let mut compiler = ListingCompiler::new(&wam.code_repo); + let mut compiler = ListingCompiler::new(&wam.code_repo, true); match compile_into_module_impl(wam, &mut compiler, module_name, src, indices) { Ok(()) => EvalSession::EntrySuccess, @@ -382,14 +415,18 @@ impl ClauseCodeGenerator { vec![Box::new(head.clone()), Box::new(tail.clone())], None, ); - PredicateClause::Fact(clause) + PredicateClause::Fact(clause, 0, 0) }) .collect(), ); let p = self.code.len() + wam.code_repo.code.len() + self.len_offset; - let mut decl_code = - compile_relation(&TopLevel::Predicate(predicate), false, wam.machine_flags())?; + let mut cg = CodeGenerator::::new(false, wam.machine_flags()); + + let mut decl_code = compile_relation( + &mut cg, + &TopLevel::Predicate(predicate), + )?; compile_appendix(&mut decl_code, &VecDeque::new(), false, wam.machine_flags())?; @@ -404,14 +441,16 @@ impl ClauseCodeGenerator { { wam.code_repo.code.extend(self.code.into_iter()); + if self.module_name.as_str() == "user" { + for ((name, arity), _) in &dynamic_code_dir { + wam.indices.code_dir.entry((name.clone(), *arity)) + .or_insert(CodeIndex::dynamic_undefined(clause_name!("user"))); + } + } + for ((name, arity), _) in dynamic_code_dir { - wam.indices.dynamic_code_dir.insert((name.owning_module(), name.clone(), arity), + wam.indices.dynamic_code_dir.insert((name.owning_module(), name, arity), DynamicPredicateInfo::default()); - - if self.module_name.as_str() == "user" { - wam.indices.code_dir.entry((name, arity)) - .or_insert(CodeIndex::dynamic_undefined(self.module_name.clone())); - } } for ((name, arity), p) in self.pi_to_loc { @@ -432,7 +471,8 @@ pub struct ListingCompiler { user_term_dir: TermDir, orig_term_expansion_lens: (usize, usize), orig_goal_expansion_lens: (usize, usize), - initialization_goals: (Vec, VecDeque) + initialization_goals: (Vec, VecDeque), + suppress_warnings: bool } fn add_toplevel_code(wam: &mut Machine, code: Code, mut indices: IndexStore) { @@ -480,11 +520,14 @@ fn add_non_module_code( } pub(super) -fn load_library(wam: &mut Machine, name: ClauseName) -> Result -{ +fn load_library( + wam: &mut Machine, + name: ClauseName, + suppress_warnings: bool, +) -> Result { match LIBRARIES.borrow().get(name.as_str()) { Some(code) => { - let module_name = load_module(wam, parsing_stream(code.as_bytes()))?; + let module_name = load_module(wam, parsing_stream(code.as_bytes()), suppress_warnings)?; module_name.ok_or(SessionError::NoModuleDeclaration(name)) } None => Err(SessionError::ModuleNotFound) @@ -493,7 +536,7 @@ fn load_library(wam: &mut Machine, name: ClauseName) -> Result Self { + pub fn new(code_repo: &CodeRepo, suppress_warnings: bool) -> Self { ListingCompiler { non_counted_bt_preds: IndexSet::new(), module: None, @@ -502,7 +545,8 @@ impl ListingCompiler { .term_dir_entry_len((clause_name!("term_expansion"), 2)), orig_goal_expansion_lens: code_repo .term_dir_entry_len((clause_name!("goal_expansion"), 2)), - initialization_goals: (vec![], VecDeque::from(vec![])) + initialization_goals: (vec![], VecDeque::from(vec![])), + suppress_warnings } } @@ -636,14 +680,17 @@ impl ListingCompiler { let non_counted_bt = self.non_counted_bt_preds.contains(&(name.clone(), arity)); let p = code.len() + wam.code_repo.code.len() + code_offset; - let mut decl_code = compile_relation( - &TopLevel::Predicate(decl), - non_counted_bt, - wam.machine_flags(), - )?; + let mut cg = CodeGenerator::::new(non_counted_bt, wam.machine_flags()); + + let decl = TopLevel::Predicate(decl); + let mut decl_code = compile_relation(&mut cg, &decl)?; compile_appendix(&mut decl_code, &queue, non_counted_bt, wam.machine_flags())?; + if !self.suppress_warnings { + issue_singleton_warnings(&decl, self.get_module_name()); + } + let idx = code_dir .entry((name.clone(), arity)) .or_insert(CodeIndex::default()); @@ -748,7 +795,7 @@ impl ListingCompiler { } Declaration::UseModule(ModuleSource::Library(name)) => { let name = if !wam.indices.modules.contains_key(&name) { - load_library(wam, name)? + load_library(wam, name, true)? } else { name }; @@ -757,7 +804,7 @@ impl ListingCompiler { } Declaration::UseQualifiedModule(ModuleSource::Library(name), exports) => { let name = if !wam.indices.modules.contains_key(&name) { - load_library(wam, name)? + load_library(wam, name, true)? } else { name }; @@ -782,7 +829,7 @@ impl ListingCompiler { } } Declaration::UseModule(ModuleSource::File(filename)) => { - let name = load_module_from_file(wam, filename.as_str())?; + let name = load_module_from_file(wam, filename.as_str(), true)?; if let Some(name) = name { self.use_module(name, &mut wam.code_repo, flags, &mut wam.indices, indices) @@ -791,7 +838,7 @@ impl ListingCompiler { } } Declaration::UseQualifiedModule(ModuleSource::File(filename), exports) => { - let name = load_module_from_file(wam, filename.as_str())?; + let name = load_module_from_file(wam, filename.as_str(), true)?; if let Some(name) = name { self.use_qualified_module( @@ -877,7 +924,7 @@ impl ListingCompiler { self.process_and_commit_decl(decl, &mut worker, indices, flags)?; - if let &Some(ref module) = &self.module { + if let Some(ref module) = &self.module { worker.term_stream.set_atom_tbl(module.atom_tbl.clone()); } } else if decl.is_end_of_file() { @@ -997,7 +1044,7 @@ pub fn compile_special_form( 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 mut compiler = ListingCompiler::new(&wam.code_repo, true); let results = compiler.gather_items(wam, src, &mut indices)?; compiler.generate_code(results.worker_results, wam, &mut indices.code_dir, 0) @@ -1008,8 +1055,9 @@ pub fn compile_listing( wam: &mut Machine, src: ParsingStream, indices: IndexStore, + suppress_warnings: bool ) -> EvalSession { - let mut compiler = ListingCompiler::new(&wam.code_repo); + let mut compiler = ListingCompiler::new(&wam.code_repo, suppress_warnings); match compile_work(&mut compiler, wam, src, indices) { EvalSession::Error(e) => { @@ -1036,8 +1084,12 @@ pub(super) fn setup_indices( } } -pub fn compile_user_module(wam: &mut Machine, src: ParsingStream) -> EvalSession { +pub fn compile_user_module( + wam: &mut Machine, + src: ParsingStream, + suppress_warnings: bool, +) -> EvalSession { let mut indices = default_index_store!(wam.indices.atom_tbl.clone()); try_eval_session!(setup_indices(wam, clause_name!("builtins"), &mut indices)); - compile_listing(wam, src, indices) + compile_listing(wam, src, indices, suppress_warnings) } diff --git a/src/prolog/machine/dynamic_database.rs b/src/prolog/machine/dynamic_database.rs index b9ea7457..9ebdcdf6 100644 --- a/src/prolog/machine/dynamic_database.rs +++ b/src/prolog/machine/dynamic_database.rs @@ -27,11 +27,11 @@ impl Machine { let module = idx.0.borrow().1.clone(); match module.as_str() { - "user" => compile_user_module(self, src), + "user" => compile_user_module(self, src, true), _ => compile_into_module(self, module, src, name), } } - None => compile_user_module(self, src), + None => compile_user_module(self, src, true), }, _ => compile_into_module(self, name.owning_module(), src, name), } diff --git a/src/prolog/machine/mod.rs b/src/prolog/machine/mod.rs index a5a65934..7247170d 100644 --- a/src/prolog/machine/mod.rs +++ b/src/prolog/machine/mod.rs @@ -193,7 +193,7 @@ impl Machine { fn compile_top_level(&mut self) { self.toplevel_idx = self.code_repo.code.len(); - compile_user_module(self, parsing_stream(TOPLEVEL.as_bytes())); + compile_user_module(self, parsing_stream(TOPLEVEL.as_bytes()), true); } fn compile_scryerrc(&mut self) { @@ -210,7 +210,7 @@ impl Machine { Err(_) => return, }; - compile_user_module(self, file_src); + compile_user_module(self, file_src, true); } } @@ -268,14 +268,15 @@ impl Machine { &mut wam, parsing_stream(BUILTINS.as_bytes()), default_index_store!(atom_tbl.clone()), + true ); wam.compile_special_forms(); - compile_user_module(&mut wam, parsing_stream(ERROR.as_bytes())); - compile_user_module(&mut wam, parsing_stream(LISTS.as_bytes())); - compile_user_module(&mut wam, parsing_stream(NON_ISO.as_bytes())); - compile_user_module(&mut wam, parsing_stream(SI.as_bytes())); + compile_user_module(&mut wam, parsing_stream(ERROR.as_bytes()), true); + compile_user_module(&mut wam, parsing_stream(LISTS.as_bytes()), true); + compile_user_module(&mut wam, parsing_stream(NON_ISO.as_bytes()), true); + compile_user_module(&mut wam, parsing_stream(SI.as_bytes()), true); wam.compile_top_level(); wam.compile_scryerrc(); @@ -430,12 +431,12 @@ impl Machine { let load_result = match to_src(name) { ModuleSource::Library(name) => if !self.indices.modules.contains_key(&name) { - load_library(self, name).map(Some) + load_library(self, name, false).map(Some) } else { Ok(Some(name)) }, ModuleSource::File(name) => - load_module_from_file(self, name.as_str()) + load_module_from_file(self, name.as_str(), false) }; let result = load_result.and_then(|name| @@ -477,12 +478,12 @@ impl Machine { let load_result = match to_src(name) { ModuleSource::Library(name) => if !self.indices.modules.contains_key(&name) { - load_library(self, name).map(Some) + load_library(self, name, false).map(Some) } else { Ok(Some(name)) }, ModuleSource::File(name) => - load_module_from_file(self, name.as_str()) + load_module_from_file(self, name.as_str(), false) }; let result = load_result.and_then(|name| @@ -515,7 +516,7 @@ impl Machine { let src = readline::input_stream(); readline::set_prompt(false); - if let EvalSession::Error(e) = compile_user_module(self, src) { + if let EvalSession::Error(e) = compile_user_module(self, src, false) { self.throw_session_error(e, (clause_name!("repl"), 0)); } } diff --git a/src/prolog/machine/term_expansion.rs b/src/prolog/machine/term_expansion.rs index 645f6fc3..931e6adc 100644 --- a/src/prolog/machine/term_expansion.rs +++ b/src/prolog/machine/term_expansion.rs @@ -150,6 +150,16 @@ impl<'a, R: Read> TermStream<'a, R> { } } + #[inline] + pub fn line_num(&self) -> usize { + self.parser.line_num() + } + + #[inline] + pub fn col_num(&self) -> usize { + self.parser.col_num() + } + #[inline] pub fn update_expansion_lens(&mut self) { let te_key = (clause_name!("term_expansion"), 2); @@ -362,7 +372,7 @@ impl MachineState { ) -> Option { let term_write_result = write_term_to_heap(term, self); let h = self.heap.h; - + self[temp_v!(1)] = Addr::HeapCell(term_write_result.heap_loc); self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h))); self[temp_v!(2)] = Addr::HeapCell(h); diff --git a/src/prolog/machine/toplevel.rs b/src/prolog/machine/toplevel.rs index 3a7c4322..12135a7b 100644 --- a/src/prolog/machine/toplevel.rs +++ b/src/prolog/machine/toplevel.rs @@ -287,15 +287,14 @@ fn merge_clauses(tls: &mut VecDeque) -> Result TopLevel::Query(_) if clauses.is_empty() && tls.is_empty() => return Ok(tl), TopLevel::Declaration(_) if clauses.is_empty() => return Ok(tl), TopLevel::Query(_) => return Err(ParserError::InconsistentEntry), - TopLevel::Fact(_) if is_consistent(&tl, &clauses) => { - if let TopLevel::Fact(fact) = tl { - let clause = PredicateClause::Fact(fact); + TopLevel::Fact(..) if is_consistent(&tl, &clauses) => + if let TopLevel::Fact(fact, line_num, col_num) = tl { + let clause = PredicateClause::Fact(fact, line_num, col_num); clauses.push(clause); - } - } - TopLevel::Rule(_) if is_consistent(&tl, &clauses) => { - if let TopLevel::Rule(rule) = tl { - let clause = PredicateClause::Rule(rule); + }, + TopLevel::Rule(..) if is_consistent(&tl, &clauses) => { + if let TopLevel::Rule(rule, line_num, col_num) = tl { + let clause = PredicateClause::Rule(rule, line_num, col_num); clauses.push(clause); } } @@ -357,6 +356,44 @@ fn mark_cut_variables(terms: &mut Vec) -> bool { found_cut_var } +// terms is a list of goals composing one clause in a (;) functor. it +// checks that the first (and only) of these clauses is a ->. if so, +// it expands its terms using a blocked_!. +fn check_for_internal_if_then(terms: &mut Vec) { + if terms.len() != 1 { + return; + } + + if let Some(Term::Clause(_, ref name, ref subterms, _)) = terms.last() { + if name.as_str() != "->" || subterms.len() != 2 { + return; + } + } else { + return; + } + + if let Some(Term::Clause(_, _, mut subterms, _)) = terms.pop() { + let mut conq_terms = VecDeque::from(unfold_by_str(*subterms.pop().unwrap(), ",")); + let mut pre_cut_terms = VecDeque::from(unfold_by_str(*subterms.pop().unwrap(), ",")); + + conq_terms.push_front(Term::Constant( + Cell::default(), + Constant::Atom(clause_name!("blocked_!"), None)) + ); + + while let Some(term) = pre_cut_terms.pop_back() { + conq_terms.push_front(term); + } + + let tail_term = conq_terms.pop_back().unwrap(); + terms.push(fold_by_str( + conq_terms.into_iter(), + tail_term, + clause_name!(","), + )); + } +} + fn flatten_hook(mut term: Term) -> Term { if let &mut Term::Clause(_, ref mut name, ref mut terms, _) = &mut term { match (name.as_str(), terms.len()) { @@ -387,7 +424,9 @@ fn flatten_hook(mut term: Term) -> Term { fn setup_declaration( indices: &mut CompositeIndices, flags: MachineFlags, - mut terms: Vec> + mut terms: Vec>, + line_num: usize, + col_num: usize, ) -> Result { let term = *terms.pop().unwrap(); @@ -413,10 +452,10 @@ fn setup_declaration( Ok(Declaration::Dynamic(name, arity)) } ("initialization", 1) => { - let mut rel_worker = RelationWorker::new(flags); + let mut rel_worker = RelationWorker::new(flags, line_num, col_num); let query_terms = rel_worker.setup_query(indices, terms, false)?; let queue = rel_worker.parse_queue(indices)?; - + Ok(Declaration::ModuleInitialization(query_terms, queue)) } _ => @@ -435,14 +474,18 @@ struct RelationWorker { flags: MachineFlags, dynamic_clauses: Vec<(Term, Term)>, // Head, Body. queue: VecDeque>, + line_num: usize, + col_num: usize } impl RelationWorker { - fn new(flags: MachineFlags) -> Self { + fn new(flags: MachineFlags, line_num: usize, col_num: usize) -> Self { RelationWorker { dynamic_clauses: vec![], flags, queue: VecDeque::new(), + line_num, + col_num } } @@ -507,6 +550,8 @@ impl RelationWorker { let mut subterms = unfold_by_str(term, ","); mark_cut_variables(&mut subterms); + check_for_internal_if_then(&mut subterms); + let term = subterms.pop().unwrap(); fold_by_str(subterms.into_iter(), term, clause_name!(",")) }) @@ -704,12 +749,12 @@ impl RelationWorker { Term::Clause(r, name, terms, _) => { if name == hook.name() && terms.len() == hook.arity() { let term = self.setup_fact(Term::Clause(r, name, terms, None), false)?; - Ok((hook, PredicateClause::Fact(term), VecDeque::from(vec![]))) + Ok((hook, PredicateClause::Fact(term, 0, 0), VecDeque::from(vec![]))) } else if name.as_str() == ":-" && terms.len() == 2 { let rule = self.setup_rule(indices, terms, true, false)?; let results_queue = self.parse_queue(indices)?; - Ok((hook, PredicateClause::Rule(rule), results_queue)) + Ok((hook, PredicateClause::Rule(rule, 0, 0), results_queue)) } else { Err(ParserError::InvalidHook) } @@ -787,15 +832,16 @@ impl RelationWorker { terms, blocks_cuts, true, - )?)) + )?, self.line_num, self.col_num)) } else if name.as_str() == ":-" && terms.len() == 1 { - Ok(TopLevel::Declaration(setup_declaration(indices, self.flags, terms)?)) + Ok(TopLevel::Declaration(setup_declaration(indices, self.flags, terms, + self.line_num, self.col_num)?)) } else { let term = Term::Clause(r, name, terms, fixity); - Ok(TopLevel::Fact(try!(self.setup_fact(term, true)))) + Ok(TopLevel::Fact(self.setup_fact(term, true)?, self.line_num, self.col_num)) } } - term => Ok(TopLevel::Fact(try!(self.setup_fact(term, true)))), + term => Ok(TopLevel::Fact(self.setup_fact(term, true)?, self.line_num, self.col_num)), } } @@ -875,7 +921,10 @@ fn term_to_toplevel( where R: Read, { - let mut rel_worker = RelationWorker::new(flags); + let line_num = term_stream.line_num(); + let col_num = term_stream.col_num(); + + let mut rel_worker = RelationWorker::new(flags, line_num, col_num); 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)?; @@ -928,9 +977,12 @@ impl<'a, R: Read> TopLevelBatchWorker<'a, R> { ) -> Self { let term_stream = TermStream::new(inner, atom_tbl, flags, wam); + let line_num = term_stream.line_num(); + let col_num = term_stream.col_num(); + TopLevelBatchWorker { term_stream, - rel_worker: RelationWorker::new(flags), + rel_worker: RelationWorker::new(flags, line_num, col_num), results: vec![], dynamic_clause_map: IndexMap::new(), in_module: false, @@ -942,7 +994,10 @@ impl<'a, R: Read> TopLevelBatchWorker<'a, R> { indices: &mut IndexStore, term: Term, ) -> Result<(TopLevel, RelationWorker), SessionError> { - let mut new_rel_worker = RelationWorker::new(self.rel_worker.flags); + let line_num = self.term_stream.line_num(); + let col_num = self.term_stream.col_num(); + + let mut new_rel_worker = RelationWorker::new(self.rel_worker.flags, line_num, col_num); let mut indices = composite_indices!( self.in_module, indices, @@ -1023,11 +1078,16 @@ impl<'a, R: Read> TopLevelBatchWorker<'a, R> { self.rel_worker.absorb(new_rel_worker); match tl { - TopLevel::Fact(fact) => preds.push(PredicateClause::Fact(fact)), - TopLevel::Rule(rule) => preds.push(PredicateClause::Rule(rule)), - TopLevel::Predicate(pred) => preds.extend(pred.0), - TopLevel::Declaration(decl) => return Ok(Some(decl)), - TopLevel::Query(_) => return Err(SessionError::NamelessEntry), + TopLevel::Fact(fact, line_num, col_num) => + preds.push(PredicateClause::Fact(fact, line_num, col_num)), + TopLevel::Rule(rule, line_num, col_num) => + preds.push(PredicateClause::Rule(rule, line_num, col_num)), + TopLevel::Predicate(pred) => + preds.extend(pred.0), + TopLevel::Declaration(decl) => + return Ok(Some(decl)), + TopLevel::Query(_) => + return Err(SessionError::NamelessEntry), } } diff --git a/src/tests.rs b/src/tests.rs index cbb7a8be..1b482e9e 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -195,7 +195,7 @@ pub fn submit_query_with_limit( #[allow(dead_code)] pub fn submit_code(wam: &mut Machine, buf: &str) -> EvalSession { - compile_user_module(wam, parsing_stream(buf.as_bytes())) + compile_user_module(wam, parsing_stream(buf.as_bytes()), true) } #[allow(unused_macros)] -- 2.54.0