use prolog_parser::ast::*; use prolog_parser::{clause_name, temp_v}; use crate::forms::{ModuleSource, Number}; //, PredicateKey}; use crate::machine::heap::*; use crate::machine::loader::CompilationTarget; use crate::machine::machine_indices::*; use crate::machine::machine_state::*; use crate::machine::PredicateKey; use crate::rug::Integer; use std::rc::Rc; pub(crate) type MachineStub = Vec; #[derive(Debug, Clone, Copy)] enum ErrorProvenance { Constructed, // if constructed, offset the addresses. Received, // otherwise, preserve the addresses. } #[derive(Debug)] pub(crate) struct MachineError { stub: MachineStub, location: Option<(usize, usize)>, // line_num, col_num from: ErrorProvenance, } pub(crate) trait TypeError { fn type_error(self, h: usize, valid_type: ValidType) -> MachineError; } impl TypeError for Addr { fn type_error(self, _: usize, valid_type: ValidType) -> MachineError { let stub = functor!("type_error", [atom(valid_type.as_str()), addr(self)]); MachineError { stub, location: None, from: ErrorProvenance::Received, } } } impl TypeError for HeapCellValue { fn type_error(self, _: usize, valid_type: ValidType) -> MachineError { let stub = functor!("type_error", [atom(valid_type.as_str()), value(self)]); MachineError { stub, location: None, from: ErrorProvenance::Received, } } } impl TypeError for MachineStub { fn type_error(self, h: usize, valid_type: ValidType) -> MachineError { let stub = functor!("type_error", [atom(valid_type.as_str()), aux(h, 0)], [self]); MachineError { stub, location: None, from: ErrorProvenance::Constructed, } } } impl TypeError for Number { fn type_error(self, _h: usize, valid_type: ValidType) -> MachineError { let stub = functor!("type_error", [atom(valid_type.as_str()), number(self)]); MachineError { stub, location: None, from: ErrorProvenance::Received, } } } pub(crate) trait PermissionError { fn permission_error(self, h: usize, index_str: &'static str, perm: Permission) -> MachineError; } impl PermissionError for Addr { fn permission_error(self, _: usize, index_str: &'static str, perm: Permission) -> MachineError { let stub = functor!( "permission_error", [atom(perm.as_str()), atom(index_str), addr(self)] ); MachineError { stub, location: None, from: ErrorProvenance::Received, } } } impl PermissionError for MachineStub { fn permission_error(self, h: usize, index_str: &'static str, perm: Permission) -> MachineError { let stub = functor!( "permission_error", [atom(perm.as_str()), atom(index_str), aux(h, 0)], [self] ); MachineError { stub, location: None, from: ErrorProvenance::Constructed, } } } pub(super) trait DomainError { fn domain_error(self, error: DomainErrorType) -> MachineError; } impl DomainError for Addr { fn domain_error(self, error: DomainErrorType) -> MachineError { let stub = functor!("domain_error", [atom(error.as_str()), addr(self)]); MachineError { stub, location: None, from: ErrorProvenance::Received, } } } impl DomainError for Number { fn domain_error(self, error: DomainErrorType) -> MachineError { let stub = functor!("domain_error", [atom(error.as_str()), number(self)]); MachineError { stub, location: None, from: ErrorProvenance::Received, } } } impl MachineError { pub(super) fn functor_stub(name: ClauseName, arity: usize) -> MachineStub { functor!( "/", SharedOpDesc::new(400, YFX), [clause_name(name), integer(arity)] ) } #[inline] pub(super) fn interrupt_error() -> Self { let stub = functor!("$interrupt_thrown"); MachineError { stub, location: None, from: ErrorProvenance::Received, } } pub(super) fn evaluation_error(eval_error: EvalError) -> Self { let stub = functor!("evaluation_error", [atom(eval_error.as_str())]); MachineError { stub, location: None, from: ErrorProvenance::Received, } } pub(super) fn type_error(h: usize, valid_type: ValidType, culprit: T) -> Self { culprit.type_error(h, valid_type) } pub(super) fn module_resolution_error( h: usize, mod_name: ClauseName, name: ClauseName, arity: usize, ) -> Self { let res_stub = functor!( ":", SharedOpDesc::new(600, XFY), [clause_name(mod_name), clause_name(name)] ); let ind_stub = functor!( "/", SharedOpDesc::new(400, YFX), [aux(h + 2, 0), integer(arity)], [res_stub] ); let stub = functor!("evaluation_error", [aux(h, 0)], [ind_stub]); MachineError { stub, location: None, from: ErrorProvenance::Constructed, } } pub(super) fn existence_error(h: usize, err: ExistenceError) -> Self { match err { ExistenceError::Module(name) => { let stub = functor!("existence_error", [atom("source_sink"), clause_name(name)]); MachineError { stub, location: None, from: ErrorProvenance::Received, } } ExistenceError::Procedure(name, arity) => { let culprit = functor!( "/", SharedOpDesc::new(400, YFX), [clause_name(name), integer(arity)] ); let stub = functor!("existence_error", [atom("procedure"), aux(h, 0)], [culprit]); MachineError { stub, location: None, from: ErrorProvenance::Constructed, } } ExistenceError::ModuleSource(source) => { let source_stub = source.as_functor_stub(); let stub = functor!( "existence_error", [atom("source_sink"), aux(h, 0)], [source_stub] ); MachineError { stub, location: None, from: ErrorProvenance::Constructed, } } ExistenceError::SourceSink(culprit) => { let stub = functor!("existence_error", [atom("source_sink"), addr(culprit)]); MachineError { stub, location: None, from: ErrorProvenance::Received, } } ExistenceError::Stream(culprit) => { let stub = functor!("existence_error", [atom("stream"), addr(culprit)]); MachineError { stub, location: None, from: ErrorProvenance::Received, } } } } pub(super) fn permission_error( h: usize, err: Permission, index_str: &'static str, culprit: T, ) -> Self { culprit.permission_error(h, index_str, err) } fn arithmetic_error(h: usize, err: ArithmeticError) -> Self { match err { ArithmeticError::UninstantiatedVar => Self::instantiation_error(), ArithmeticError::NonEvaluableFunctor(name, arity) => { let culprit = functor!( "/", SharedOpDesc::new(400, YFX), [constant(h, &name), integer(arity)] ); Self::type_error(h, ValidType::Evaluable, culprit) } } } #[inline] pub(super) fn domain_error(error: DomainErrorType, culprit: T) -> Self { culprit.domain_error(error) } pub(super) fn instantiation_error() -> Self { let stub = functor!("instantiation_error"); MachineError { stub, location: None, from: ErrorProvenance::Received, } } pub(super) fn session_error(h: usize, err: SessionError) -> Self { match err { // SessionError::CannotOverwriteBuiltIn(pred_str) | /* SessionError::CannotOverwriteImport(pred_str) => { Self::permission_error( h, Permission::Modify, "private_procedure", functor!(clause_name(pred_str)), ) } */ SessionError::ExistenceError(err) => Self::existence_error(h, err), // SessionError::InvalidFileName(filename) => { // Self::existence_error(h, ExistenceError::Module(filename)) // } SessionError::ModuleDoesNotContainExport(..) => Self::permission_error( h, Permission::Access, "private_procedure", functor!("module_does_not_contain_claimed_export"), ), SessionError::ModuleCannotImportSelf(module_name) => Self::permission_error( h, Permission::Modify, "module", functor!("module_cannot_import_self", [clause_name(module_name)]), ), SessionError::NamelessEntry => Self::permission_error( h, Permission::Create, "static_procedure", functor!("nameless_procedure"), ), SessionError::OpIsInfixAndPostFix(op) => { Self::permission_error(h, Permission::Create, "operator", functor!(clause_name(op))) } SessionError::CompilationError(err) => Self::syntax_error(h, err), SessionError::PredicateNotMultifileOrDiscontiguous(compilation_target, key) => { let functor_stub = Self::functor_stub(key.0, key.1); let stub = functor!( ":", SharedOpDesc::new(600, XFY), [clause_name(compilation_target.module_name()), aux(h + 4, 0)], [functor_stub] ); Self::permission_error( h, Permission::Modify, "not_declared_multifile_or_discontiguous", stub, ) } SessionError::QueryCannotBeDefinedAsFact => Self::permission_error( h, Permission::Create, "static_procedure", functor!("query_cannot_be_defined_as_fact"), ), } } pub(super) fn syntax_error>(h: usize, err: E) -> Self { let err = err.into(); if let CompilationError::Arithmetic(err) = err { return Self::arithmetic_error(h, err); } let location = err.line_and_col_num(); let stub = err.as_functor(h); let stub = functor!("syntax_error", [aux(h, 0)], [stub]); MachineError { stub, location, from: ErrorProvenance::Constructed, } } pub(super) fn representation_error(flag: RepFlag) -> Self { let stub = functor!("representation_error", [atom(flag.as_str())]); MachineError { stub, location: None, from: ErrorProvenance::Received, } } fn into_iter(self, offset: usize) -> Box> { match self.from { ErrorProvenance::Constructed => { Box::new(self.stub.into_iter().map(move |hcv| match hcv { HeapCellValue::Addr(addr) => HeapCellValue::Addr(addr + offset), hcv => hcv, })) } ErrorProvenance::Received => Box::new(self.stub.into_iter()), } } fn len(&self) -> usize { self.stub.len() } } #[derive(Debug)] pub(crate) enum CompilationError { Arithmetic(ArithmeticError), ParserError(ParserError), // BadPendingByte, CannotParseCyclicTerm, // ExpandedTermsListNotAList, ExpectedRel, // ExpectedTopLevelTerm, InadmissibleFact, InadmissibleQueryTerm, InconsistentEntry, // InvalidDoubleQuotesDecl, // InvalidHook, InvalidMetaPredicateDecl, InvalidModuleDecl, InvalidModuleExport, InvalidRuleHead, InvalidUseModuleDecl, InvalidModuleResolution(ClauseName), UnreadableTerm, } impl From for CompilationError { #[inline] fn from(err: ArithmeticError) -> CompilationError { CompilationError::Arithmetic(err) } } impl From for CompilationError { #[inline] fn from(err: ParserError) -> CompilationError { CompilationError::ParserError(err) } } impl CompilationError { pub(crate) fn line_and_col_num(&self) -> Option<(usize, usize)> { match self { &CompilationError::ParserError(ref err) => err.line_and_col_num(), _ => None, } } pub(crate) fn as_functor(&self, _h: usize) -> MachineStub { match self { &CompilationError::Arithmetic(..) => functor!("arithmetic_error"), // &CompilationError::BadPendingByte => // functor!("bad_pending_byte"), &CompilationError::CannotParseCyclicTerm => functor!("cannot_parse_cyclic_term"), // &CompilationError::ExpandedTermsListNotAList => // functor!("expanded_terms_list_is_not_a_list"), &CompilationError::ExpectedRel => functor!("expected_relation"), // &CompilationError::ExpectedTopLevelTerm => // functor!("expected_atom_or_cons_or_clause"), &CompilationError::InadmissibleFact => functor!("inadmissible_fact"), &CompilationError::InadmissibleQueryTerm => functor!("inadmissible_query_term"), &CompilationError::InconsistentEntry => functor!("inconsistent_entry"), // &CompilationError::InvalidDoubleQuotesDecl => // functor!("invalid_double_quotes_declaration"), // &CompilationError::InvalidHook => // functor!("invalid_hook"), &CompilationError::InvalidMetaPredicateDecl => functor!("invalid_meta_predicate_decl"), &CompilationError::InvalidModuleDecl => functor!("invalid_module_declaration"), &CompilationError::InvalidModuleExport => functor!("invalid_module_export"), &CompilationError::InvalidModuleResolution(ref module_name) => { functor!("no_such_module", [clause_name(module_name.clone())]) } &CompilationError::InvalidRuleHead => functor!("invalid_head_of_rule"), &CompilationError::InvalidUseModuleDecl => functor!("invalid_use_module_declaration"), &CompilationError::ParserError(ref err) => functor!(err.as_str()), &CompilationError::UnreadableTerm => functor!("unreadable_term"), } } } #[derive(Debug, Clone, Copy)] pub(crate) enum Permission { Access, Create, InputStream, Modify, Open, OutputStream, Reposition, } impl Permission { #[inline] pub(crate) fn as_str(self) -> &'static str { match self { Permission::Access => "access", Permission::Create => "create", Permission::InputStream => "input", Permission::Modify => "modify", Permission::Open => "open", Permission::OutputStream => "output", Permission::Reposition => "reposition", } } } // from 7.12.2 b) of 13211-1:1995 #[derive(Debug, Clone, Copy)] pub(crate) enum ValidType { Atom, Atomic, // Boolean, Byte, Callable, Character, Compound, Evaluable, Float, InByte, InCharacter, Integer, List, Number, Pair, // PredicateIndicator, // Variable TcpListener, } impl ValidType { pub(crate) fn as_str(self) -> &'static str { match self { ValidType::Atom => "atom", ValidType::Atomic => "atomic", // ValidType::Boolean => "boolean", ValidType::Byte => "byte", ValidType::Callable => "callable", ValidType::Character => "character", ValidType::Compound => "compound", ValidType::Evaluable => "evaluable", ValidType::Float => "float", ValidType::InByte => "in_byte", ValidType::InCharacter => "in_character", ValidType::Integer => "integer", ValidType::List => "list", ValidType::Number => "number", ValidType::Pair => "pair", // ValidType::PredicateIndicator => "predicate_indicator", // ValidType::Variable => "variable" ValidType::TcpListener => "tcp_listener", } } } #[derive(Debug, Clone, Copy)] pub(crate) enum DomainErrorType { IOMode, NotLessThanZero, Order, SourceSink, Stream, StreamOrAlias, } impl DomainErrorType { pub(crate) fn as_str(self) -> &'static str { match self { DomainErrorType::IOMode => "io_mode", DomainErrorType::NotLessThanZero => "not_less_than_zero", DomainErrorType::Order => "order", DomainErrorType::SourceSink => "source_sink", DomainErrorType::Stream => "stream", DomainErrorType::StreamOrAlias => "stream_or_alias", } } } // from 7.12.2 f) of 13211-1:1995 #[derive(Debug, Clone, Copy)] pub(crate) enum RepFlag { Character, CharacterCode, InCharacterCode, MaxArity, // MaxInteger, // MinInteger, Term, } impl RepFlag { pub(crate) fn as_str(self) -> &'static str { match self { RepFlag::Character => "character", RepFlag::CharacterCode => "character_code", RepFlag::InCharacterCode => "in_character_code", RepFlag::MaxArity => "max_arity", RepFlag::Term => "term", // RepFlag::MaxInteger => "max_integer", // RepFlag::MinInteger => "min_integer" } } } // from 7.12.2 g) of 13211-1:1995 #[derive(Debug, Clone, Copy)] pub(crate) enum EvalError { FloatOverflow, Undefined, // Underflow, ZeroDivisor, } impl EvalError { pub(crate) fn as_str(self) -> &'static str { match self { EvalError::FloatOverflow => "float_overflow", EvalError::Undefined => "undefined", // EvalError::FloatUnderflow => "underflow", EvalError::ZeroDivisor => "zero_divisor", } } } // used by '$skip_max_list'. #[derive(Debug, Clone, Copy)] pub(super) enum CycleSearchResult { EmptyList, NotList, PartialList(usize, Ref), // the list length (up to max), and an offset into the heap. ProperList(usize), // the list length. PStrLocation(usize, usize, usize), // the list length (up to max), the heap offset, byte offset into the string. UntouchedList(usize), // the address of an uniterated Addr::Lis(address). } impl MachineState { // see 8.4.3 of Draft Technical Corrigendum 2. pub(super) fn check_sort_errors(&self) -> CallResult { let stub = MachineError::functor_stub(clause_name!("sort"), 2); let list = self.store(self.deref(self[temp_v!(1)].clone())); let sorted = self.store(self.deref(self[temp_v!(2)].clone())); match self.detect_cycles(list.clone()) { CycleSearchResult::PartialList(..) => { return Err(self.error_form(MachineError::instantiation_error(), stub)) } CycleSearchResult::NotList => { return Err( self.error_form(MachineError::type_error(0, ValidType::List, list), stub) ) } _ => {} }; match self.detect_cycles(sorted.clone()) { CycleSearchResult::NotList if !sorted.is_ref() => { Err(self.error_form(MachineError::type_error(0, ValidType::List, sorted), stub)) } _ => Ok(()), } } fn check_for_list_pairs(&self, list: Addr) -> CallResult { let stub = MachineError::functor_stub(clause_name!("keysort"), 2); match self.detect_cycles(list.clone()) { CycleSearchResult::NotList if !list.is_ref() => { Err(self.error_form(MachineError::type_error(0, ValidType::List, list), stub)) } _ => { let mut addr = list; while let Addr::Lis(l) = self.store(self.deref(addr)) { let mut new_l = l; loop { match self.heap.clone(new_l) { HeapCellValue::Addr(Addr::Str(l)) => { new_l = l; } HeapCellValue::NamedStr(2, ref name, Some(_)) if name.as_str() == "-" => { break; } HeapCellValue::Addr(Addr::HeapCell(_)) => { break; } HeapCellValue::Addr(Addr::StackCell(..)) => { break; } _ => { return Err(self.error_form( MachineError::type_error(0, ValidType::Pair, Addr::HeapCell(l)), stub, )) } }; } addr = Addr::HeapCell(l + 1); } Ok(()) } } } // see 8.4.4 of Draft Technical Corrigendum 2. pub(super) fn check_keysort_errors(&self) -> CallResult { let stub = MachineError::functor_stub(clause_name!("keysort"), 2); let pairs = self.store(self.deref(self[temp_v!(1)].clone())); let sorted = self.store(self.deref(self[temp_v!(2)].clone())); match self.detect_cycles(pairs.clone()) { CycleSearchResult::PartialList(..) => { Err(self.error_form(MachineError::instantiation_error(), stub)) } CycleSearchResult::NotList => { Err(self.error_form(MachineError::type_error(0, ValidType::List, pairs), stub)) } _ => Ok(()), }?; self.check_for_list_pairs(sorted) } #[inline] pub(crate) fn type_error( &self, valid_type: ValidType, culprit: T, caller: ClauseName, arity: usize, ) -> MachineStub { let stub = MachineError::functor_stub(caller, arity); let err = MachineError::type_error(self.heap.h(), valid_type, culprit); return self.error_form(err, stub); } #[inline] pub(crate) fn representation_error( &self, rep_flag: RepFlag, caller: ClauseName, arity: usize, ) -> MachineStub { let stub = MachineError::functor_stub(caller, arity); let err = MachineError::representation_error(rep_flag); return self.error_form(err, stub); } pub(super) fn error_form(&self, err: MachineError, src: MachineStub) -> MachineStub { let location = err.location; let err_len = err.len(); let h = self.heap.h(); let mut stub = vec![ HeapCellValue::NamedStr(2, clause_name!("error"), None), HeapCellValue::Addr(Addr::HeapCell(h + 3)), HeapCellValue::Addr(Addr::HeapCell(h + 3 + err_len)), ]; stub.extend(err.into_iter(3)); if let Some((line_num, _)) = location { let colon_op_desc = Some(SharedOpDesc::new(600, XFY)); stub.push(HeapCellValue::NamedStr(2, clause_name!(":"), colon_op_desc)); stub.push(HeapCellValue::Addr(Addr::HeapCell(h + 6 + err_len))); stub.push(HeapCellValue::Integer(Rc::new(Integer::from(line_num)))); } stub.extend(src.into_iter()); stub } pub(super) fn throw_exception(&mut self, err: MachineStub) { let h = self.heap.h(); self.ball.boundary = 0; self.ball.stub.truncate(0); self.heap.append(err); self.registers[1] = Addr::HeapCell(h); self.set_ball(); self.unwind_stack(); } } #[derive(Debug)] pub(crate) enum ExistenceError { Module(ClauseName), ModuleSource(ModuleSource), Procedure(ClauseName, usize), SourceSink(Addr), Stream(Addr), } #[derive(Debug)] pub(crate) enum SessionError { CompilationError(CompilationError), // CannotOverwriteBuiltIn(ClauseName), // CannotOverwriteImport(ClauseName), ExistenceError(ExistenceError), // InvalidFileName(ClauseName), ModuleDoesNotContainExport(ClauseName, PredicateKey), ModuleCannotImportSelf(ClauseName), NamelessEntry, OpIsInfixAndPostFix(ClauseName), PredicateNotMultifileOrDiscontiguous(CompilationTarget, PredicateKey), QueryCannotBeDefinedAsFact, } #[derive(Debug)] pub(crate) enum EvalSession { // EntrySuccess, Error(SessionError), } impl From for EvalSession { #[inline] fn from(err: SessionError) -> Self { EvalSession::Error(err) } } impl From for SessionError { #[inline] fn from(err: std::io::Error) -> SessionError { SessionError::from(ParserError::from(err)) } } impl From for SessionError { #[inline] fn from(err: ParserError) -> Self { SessionError::CompilationError(CompilationError::from(err)) } } impl From for SessionError { #[inline] fn from(err: CompilationError) -> Self { SessionError::CompilationError(err) } } impl From for EvalSession { #[inline] fn from(err: ParserError) -> Self { EvalSession::from(SessionError::from(err)) } }