From: Mark Thom Date: Fri, 4 Oct 2019 21:32:26 +0000 (-0600) Subject: print errors with line numbers in GNU style X-Git-Tag: v0.8.110~18 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=d4f9d181498d878f85c32287f517083eb4d5e88a;p=scryer-prolog.git print errors with line numbers in GNU style --- diff --git a/src/prolog/lib/dcgs.pl b/src/prolog/lib/dcgs.pl index 3dea6bb3..626a6b8b 100644 --- a/src/prolog/lib/dcgs.pl +++ b/src/prolog/lib/dcgs.pl @@ -20,7 +20,7 @@ phrase(G, Ls0, Ls2) :- nonvar(G), G = (G1, G2), !, phrase(G1, Ls0, Ls1), phrase(G2, Ls1, Ls2). phrase(G, Ls0, Ls1) :- - nonvar(G), G == !, !, Ls0 = [], Ls1 = []. + nonvar(G), G == !, !, Ls0 = Ls1. phrase(G, Ls0, Ls1) :- call(G, Ls0, Ls1). diff --git a/src/prolog/lib/terms.pl b/src/prolog/lib/terms.pl index 8391457b..d9f41528 100644 --- a/src/prolog/lib/terms.pl +++ b/src/prolog/lib/terms.pl @@ -1,10 +1,11 @@ :- module(terms, [numbervars/3]). :- use_module(library(error)). - + numbervars(Term, N0, N) :- - catch(internal_numbervars(Term, N0, N), error(E,Ctx), - ( ( var(Ctx) -> Ctx = numbervars/3 ; true ), throw(error(E,Ctx) ) ) ). + catch(internal_numbervars(Term, N0, N), + error(E,Ctx), + ( ( var(Ctx) -> Ctx = numbervars/3 ; true ), throw(error(E,Ctx) ) ) ). internal_numbervars(Term, N0, N) :- must_be(integer, N0), @@ -12,7 +13,7 @@ internal_numbervars(Term, N0, N) :- term_variables(Term, Vars), numberlist(Vars, N0, N). -numberlist([], N,N). -numberlist(['$VAR'(N0)|Vars], N0,N) :- +numberlist([], N, N). +numberlist(['$VAR'(N0)|Vars], N0, N) :- N1 is N0+1, - numberlist(Vars, N1,N). + numberlist(Vars, N1, N). diff --git a/src/prolog/machine/compile.rs b/src/prolog/machine/compile.rs index b946951a..11b80f74 100644 --- a/src/prolog/machine/compile.rs +++ b/src/prolog/machine/compile.rs @@ -61,26 +61,35 @@ fn load_module( wam: &mut Machine, stream: ParsingStream, suppress_warnings: bool, + listing_src: ClauseName, ) -> 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, + suppress_warnings, + listing_src, + ); - let mut compiler = ListingCompiler::new(&wam.code_repo, suppress_warnings); - let results = compiler.gather_items(wam, stream, &mut indices)?; - + let results = compiler.gather_items(wam, stream, &mut indices); + let module_name = if let Some(ref module) = &compiler.module { Some(module.module_decl.name.clone()) } else { None }; - match compile_work_impl(&mut compiler, wam, indices, results) { - EvalSession::Error(e) => Err(e), - _ => Ok(module_name), - } + results.and_then(|results| compile_work_impl(&mut compiler, wam, indices, results)) + .or_else(|e| { + compiler.print_error(&e); + Err(e) + })?; + + Ok(module_name) } pub(super) @@ -90,13 +99,13 @@ fn load_module_from_file( suppress_warnings: bool, ) -> Result, SessionError> { let path = fix_filename(wam.indices.atom_tbl.clone(), filename)?; + let filename = clause_name!(path.to_string_lossy().to_string(), wam.indices.atom_tbl); 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)) + Err(SessionError::InvalidFileName(filename.clone())) })?; - load_module(wam, parsing_stream(file_handle), suppress_warnings) + load_module(wam, parsing_stream(file_handle), suppress_warnings, filename) } pub type PredicateCompileQueue = (Predicate, VecDeque); @@ -115,7 +124,7 @@ fn compile_relation( } fn issue_singleton_warnings( - module_name: ClauseName, + src_name: ClauseName, terms_and_locs: Vec<(Term, usize, usize)>, ) { for (term, line_num, _col_num) in terms_and_locs { @@ -128,7 +137,7 @@ fn issue_singleton_warnings( *entry += 1; } } - + for (var, count) in var_count { if count == 1 && !var.starts_with("_") && var.as_str() != "!" { singletons.push(var); @@ -137,12 +146,12 @@ fn issue_singleton_warnings( if let Some(last_var) = singletons.pop() { print!("Warning: {}:{}: Singleton variables: [", - module_name, line_num); - + src_name, line_num); + for var in singletons { print!("{}, ", var); } - + println!("{}]", last_var); } } @@ -247,7 +256,7 @@ pub fn compile_term(wam: &mut Machine, packet: TopLevelPacket) -> EvalSession { } } TopLevelPacket::Decl(TopLevel::Declaration(decl), _) => { - let mut compiler = ListingCompiler::new(&wam.code_repo, false); + let mut compiler = ListingCompiler::new(&wam.code_repo, false, clause_name!("user")); let indices = try_eval_session!(compile_decl(wam, &mut compiler, decl)); try_eval_session!(wam.check_toplevel_code(&indices)); @@ -332,7 +341,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, true); + let mut compiler = ListingCompiler::new(&wam.code_repo, true, module_name.clone()); match compile_into_module_impl(wam, &mut compiler, module_name, src, indices) { Ok(()) => EvalSession::EntrySuccess, @@ -478,7 +487,8 @@ pub struct ListingCompiler { orig_term_expansion_lens: (usize, usize), orig_goal_expansion_lens: (usize, usize), initialization_goals: (Vec, VecDeque), - suppress_warnings: bool + suppress_warnings: bool, + listing_src: ClauseName // a file? a module? } fn add_toplevel_code(wam: &mut Machine, code: Code, mut indices: IndexStore) { @@ -533,7 +543,8 @@ fn load_library( ) -> Result { match LIBRARIES.borrow().get(name.as_str()) { Some(code) => { - let module_name = load_module(wam, parsing_stream(code.as_bytes()), suppress_warnings)?; + let module_name = load_module(wam, parsing_stream(code.as_bytes()), + suppress_warnings, name.clone())?; module_name.ok_or(SessionError::NoModuleDeclaration(name)) } None => Err(SessionError::ModuleNotFound) @@ -542,7 +553,11 @@ fn load_library( impl ListingCompiler { #[inline] - pub fn new(code_repo: &CodeRepo, suppress_warnings: bool) -> Self { + pub fn new( + code_repo: &CodeRepo, + suppress_warnings: bool, + listing_src: ClauseName, + ) -> Self { ListingCompiler { non_counted_bt_preds: IndexSet::new(), module: None, @@ -552,7 +567,8 @@ impl ListingCompiler { orig_goal_expansion_lens: code_repo .term_dir_entry_len((clause_name!("goal_expansion"), 2)), initialization_goals: (vec![], VecDeque::from(vec![])), - suppress_warnings + suppress_warnings, + listing_src } } @@ -958,6 +974,14 @@ impl ListingCompiler { discard_result!(code_repo.compile_hook(CompileTimeHook::UserGoalExpansion, flags)); discard_result!(code_repo.compile_hook(CompileTimeHook::UserTermExpansion, flags)); } + + fn print_error(&self, e: &SessionError) { + if let &SessionError::ParserError(ref e) = e { + if let Some((line_num, _col_num)) = e.line_and_col_num() { + println!("{}:{}: {}", self.listing_src, line_num, e.as_str()); + } + } + } } fn compile_work_impl( @@ -965,19 +989,19 @@ fn compile_work_impl( wam: &mut Machine, mut indices: IndexStore, mut results: GatherResult, -) -> EvalSession { - let module_code = try_eval_session!(compiler.generate_code( +) -> Result<(), SessionError> { + let module_code = compiler.generate_code( results.worker_results, wam, &mut indices.code_dir, 0 - )); - let toplvl_code = try_eval_session!(compiler.generate_code( + )?; + let toplvl_code = compiler.generate_code( results.toplevel_results, wam, &mut results.toplevel_indices.code_dir, module_code.len() - )); + )?; if let Some(ref mut module) = &mut compiler.module { module.user_term_expansions = results.addition_results.take_term_expansions(); @@ -986,49 +1010,46 @@ fn compile_work_impl( let flags = wam.machine_flags(); - try_eval_session!(wam - .code_repo - .compile_hook(CompileTimeHook::UserTermExpansion, flags)); - try_eval_session!(wam - .code_repo - .compile_hook(CompileTimeHook::UserGoalExpansion, flags)); + wam.code_repo.compile_hook(CompileTimeHook::UserTermExpansion, flags)?; + wam.code_repo.compile_hook(CompileTimeHook::UserGoalExpansion, flags)?; if let Some(module) = compiler.module.take() { let mut clause_code_generator = ClauseCodeGenerator::new(module_code.len() + toplvl_code.len(), module.module_decl.name.clone()); - try_eval_session!(wam.check_toplevel_code(&results.toplevel_indices)); - try_eval_session!( - clause_code_generator.generate_clause_code(&results.dynamic_clause_map, wam) - ); + wam.check_toplevel_code(&results.toplevel_indices)?; + clause_code_generator.generate_clause_code(&results.dynamic_clause_map, wam)?; add_module_code(wam, module, module_code, indices); add_toplevel_code(wam, toplvl_code, results.toplevel_indices); clause_code_generator.add_clause_code(wam, results.dynamic_clause_map); } else { - try_eval_session!(add_non_module_code( + add_non_module_code( wam, results.dynamic_clause_map, module_code, indices - )); + )?; } - let init_goal_code = try_eval_session!(compiler.generate_init_goal_code( + let init_goal_code = compiler.generate_init_goal_code( wam.machine_flags() - )); + )?; if init_goal_code.len() > 0 { wam.run_init_code(init_goal_code); } if !compiler.suppress_warnings { - issue_singleton_warnings(compiler.get_module_name(), results.top_level_terms); + issue_singleton_warnings( + compiler.listing_src.clone(), + results.top_level_terms, + ); } - - EvalSession::EntrySuccess + + Ok(()) } fn compile_work( @@ -1038,7 +1059,8 @@ fn compile_work( mut indices: IndexStore, ) -> EvalSession { let results = try_eval_session!(compiler.gather_items(wam, src, &mut indices)); - compile_work_impl(compiler, wam, indices, results) + try_eval_session!(compile_work_impl(compiler, wam, indices, results)); + EvalSession::EntrySuccess } /* This is a truncated version of compile_user_module, used for @@ -1047,11 +1069,12 @@ M:verify_attributes on attributed variables. */ pub fn compile_special_form( wam: &mut Machine, src: ParsingStream, + listing_src: ClauseName, ) -> Result { 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, true); + let mut compiler = ListingCompiler::new(&wam.code_repo, true, listing_src); let results = compiler.gather_items(wam, src, &mut indices)?; compiler.generate_code(results.worker_results, wam, &mut indices.code_dir, 0) @@ -1062,13 +1085,16 @@ pub fn compile_listing( wam: &mut Machine, src: ParsingStream, indices: IndexStore, - suppress_warnings: bool + suppress_warnings: bool, + listing_src: ClauseName, ) -> EvalSession { - let mut compiler = ListingCompiler::new(&wam.code_repo, suppress_warnings); + let mut compiler = ListingCompiler::new(&wam.code_repo, suppress_warnings, listing_src); match compile_work(&mut compiler, wam, src, indices) { EvalSession::Error(e) => { compiler.drop_expansions(wam.machine_flags(), &mut wam.code_repo); + compiler.print_error(&e); + EvalSession::Error(e) } result => result, @@ -1095,8 +1121,9 @@ pub fn compile_user_module( wam: &mut Machine, src: ParsingStream, suppress_warnings: bool, + listing_src: ClauseName, ) -> 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, suppress_warnings) + compile_listing(wam, src, indices, suppress_warnings, listing_src) } diff --git a/src/prolog/machine/dynamic_database.rs b/src/prolog/machine/dynamic_database.rs index 9ebdcdf6..de6352fc 100644 --- a/src/prolog/machine/dynamic_database.rs +++ b/src/prolog/machine/dynamic_database.rs @@ -21,17 +21,19 @@ impl Machine { name: ClauseName, arity: usize, ) -> EvalSession { + let user_src = clause_name!("user"); + match name.owning_module().as_str() { "user" => match self.indices.code_dir.get(&(name.clone(), arity)).cloned() { Some(idx) => { let module = idx.0.borrow().1.clone(); match module.as_str() { - "user" => compile_user_module(self, src, true), - _ => compile_into_module(self, module, src, name), + "user" => compile_user_module(self, src, true, user_src), + _ => compile_into_module(self, module, src, name) } } - None => compile_user_module(self, src, true), + None => compile_user_module(self, src, true, user_src), }, _ => compile_into_module(self, name.owning_module(), src, name), } diff --git a/src/prolog/machine/mod.rs b/src/prolog/machine/mod.rs index a77268c2..35c1d6e2 100644 --- a/src/prolog/machine/mod.rs +++ b/src/prolog/machine/mod.rs @@ -174,7 +174,11 @@ static TOPLEVEL: &str = include_str!("../toplevel.pl"); impl Machine { fn compile_special_forms(&mut self) { - match compile_special_form(self, parsing_stream(VERIFY_ATTRS.as_bytes())) { + let verify_attrs_src = clause_name!("verify_attrs.pl"); + let project_attrs_src = clause_name!("project_attributes.pl"); + + match compile_special_form(self, parsing_stream(VERIFY_ATTRS.as_bytes()), verify_attrs_src) + { Ok(code) => { self.machine_st.attr_var_init.verify_attrs_loc = self.code_repo.code.len(); self.code_repo.code.extend(code.into_iter()); @@ -182,7 +186,8 @@ impl Machine { Err(_) => panic!("Machine::compile_special_forms() failed at VERIFY_ATTRS"), } - match compile_special_form(self, parsing_stream(PROJECT_ATTRS.as_bytes())) { + match compile_special_form(self, parsing_stream(PROJECT_ATTRS.as_bytes()), project_attrs_src) + { Ok(code) => { self.machine_st.attr_var_init.project_attrs_loc = self.code_repo.code.len(); self.code_repo.code.extend(code.into_iter()); @@ -193,7 +198,8 @@ 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()), true); + compile_user_module(self, parsing_stream(TOPLEVEL.as_bytes()), + true, clause_name!("toplevel.pl")); } fn compile_scryerrc(&mut self) { @@ -210,7 +216,8 @@ impl Machine { Err(_) => return, }; - compile_user_module(self, file_src, true); + compile_user_module(self, file_src, true, + clause_name!("$HOME/.scryerrc")); } } @@ -224,7 +231,7 @@ impl Machine { pub fn run_init_code(&mut self, code: Code) { let old_machine_st = self.machine_st.sink_to_snapshot(); self.machine_st.reset(); - + self.code_repo.cached_query = code; self.run_query(&AllocVarDict::new()); @@ -268,16 +275,21 @@ impl Machine { &mut wam, parsing_stream(BUILTINS.as_bytes()), default_index_store!(atom_tbl.clone()), - true + true, + clause_name!("builtins.pl"), ); wam.compile_special_forms(); - 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); - + compile_user_module(&mut wam, parsing_stream(ERROR.as_bytes()), true, + clause_name!("error")); + compile_user_module(&mut wam, parsing_stream(LISTS.as_bytes()), true, + clause_name!("lists")); + compile_user_module(&mut wam, parsing_stream(NON_ISO.as_bytes()), true, + clause_name!("non_iso")); + compile_user_module(&mut wam, parsing_stream(SI.as_bytes()), true, + clause_name!("si")); + wam.compile_top_level(); wam.compile_scryerrc(); @@ -371,9 +383,9 @@ impl Machine { } } - pub fn throw_session_error(&mut self, err: SessionError, key: PredicateKey) { + fn throw_session_error(&mut self, err: SessionError, key: PredicateKey) { let h = self.machine_st.heap.h; - + let err = MachineError::session_error(h, err); let stub = MachineError::functor_stub(key.0, key.1); let err = self.machine_st.error_form(err, stub); @@ -447,13 +459,13 @@ impl Machine { self.indices.remove_module(clause_name!("user"), &module); self.indices.use_module(&mut self.code_repo, self.machine_st.flags, &module)?; - + Ok(self.indices.insert_module(module)) } else { Ok(()) } - ); - + ); + self.code_repo.cached_query = cached_query; if let Err(e) = result { @@ -496,7 +508,7 @@ impl Machine { self.machine_st.flags, &module, &exports)?; - + Ok(self.indices.insert_module(module)) } else { Ok(()) @@ -513,10 +525,12 @@ impl Machine { fn handle_toplevel_command(&mut self, code_ptr: REPLCodePtr, p: LocalCodePtr) { match code_ptr { REPLCodePtr::CompileBatch => { + let user_src = clause_name!("user"); + let src = readline::input_stream(); readline::set_prompt(false); - if let EvalSession::Error(e) = compile_user_module(self, src, false) { + if let EvalSession::Error(e) = compile_user_module(self, src, false, user_src) { self.throw_session_error(e, (clause_name!("repl"), 0)); } } @@ -642,7 +656,7 @@ impl Machine { raw_stdout.flush().unwrap(); next_keypress() }; - + let result = match keypress { ContinueResult::ContinueQuery => { print!(" ;\r\n"); @@ -713,7 +727,7 @@ impl Machine { self.machine_st.absorb_snapshot(snapshot); } - + pub(super) fn run_query(&mut self, alloc_locs: &AllocVarDict) { self.machine_st.cp = LocalCodePtr::TopLevel(0, self.code_repo.size_of_cached_query()); let end_ptr = CodePtr::Local(self.machine_st.cp); diff --git a/src/prolog/toplevel.pl b/src/prolog/toplevel.pl index 0bd6ed74..443ebda7 100644 --- a/src/prolog/toplevel.pl +++ b/src/prolog/toplevel.pl @@ -31,9 +31,13 @@ !. '$print_exception'(E) :- - write_term('caught: ', [quoted(false)]), - writeq(E), - nl. + ( E = error(_, _:_) -> true % if the error source contains a line + % number, a GNU-style error message + % is expected to be printed instead. + ; write_term('caught: ', [quoted(false)]), + writeq(E), + nl + ). '$predicate_indicator'(Source, PI) :- ( nonvar(PI) -> diff --git a/src/tests.rs b/src/tests.rs index 1b482e9e..4f88b335 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,4 +1,4 @@ -use prolog_parser::ast::parsing_stream; +use prolog_parser::ast::{ClauseName, parsing_stream}; use prolog::heap_print::*; use prolog::machine::compile::*; @@ -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()), true) + compile_user_module(wam, parsing_stream(buf.as_bytes()), true, clause_name!("tests")) } #[allow(unused_macros)]