use prolog_parser::ast::*; use prolog_parser::tabled_rc::*; use prolog::clause_types::*; use prolog::machine::machine_errors::*; use prolog::machine::machine_indices::*; use std::cell::Cell; use std::collections::{HashMap, VecDeque}; use std::rc::Rc; pub type PredicateKey = (ClauseName, usize); // name, arity. // vars of predicate, toplevel offset. Vec is always a vector // of vars (we get their adjoining cells this way). pub type JumpStub = Vec; #[derive(Clone)] pub enum TopLevel { Declaration(Declaration), Fact(Term), Predicate(Predicate), Query(Vec), Rule(Rule), } impl TopLevel { pub fn name(&self) -> Option { match self { &TopLevel::Declaration(_) => None, &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()) } } pub fn arity(&self) -> usize { match self { &TopLevel::Declaration(_) => 0, &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() } } } #[derive(Clone, Copy)] pub enum Level { Deep, Root, Shallow } impl Level { pub fn child_level(self) -> Level { match self { Level::Root => Level::Shallow, _ => Level::Deep } } } #[derive(Clone)] pub enum QueryTerm { // register, clause type, subterms, use default call policy. Clause(Cell, ClauseType, Vec>, bool), BlockedCut, // a cut which is 'blocked by letters', like the P term in P -> Q. UnblockedCut(Cell), GetLevelAndUnify(Cell, Rc), Jump(JumpStub) } impl QueryTerm { pub fn set_default_caller(&mut self) { match self { &mut QueryTerm::Clause(_, _, _, ref mut use_default_cp) => *use_default_cp = true, _ => {} } } pub fn arity(&self) -> usize { match self { &QueryTerm::Clause(_, _, ref subterms, ..) => subterms.len(), &QueryTerm::BlockedCut | &QueryTerm::UnblockedCut(..) => 0, &QueryTerm::Jump(ref vars) => vars.len(), &QueryTerm::GetLevelAndUnify(..) => 1, } } } #[derive(Clone)] pub struct Rule { pub head: (ClauseName, Vec>, QueryTerm), pub clauses: Vec } #[derive(Clone)] pub struct Predicate(pub Vec); impl Predicate { #[inline] pub fn new() -> Self { Predicate(vec![]) } #[inline] pub fn clauses(self) -> Vec { self.0 } #[inline] pub fn predicate_indicator(&self) -> Option<(ClauseName, usize)> { self.0.first() .and_then(|clause| clause.name().map(|name| (name, clause.arity()))) } } pub type CompiledResult = (Predicate, VecDeque); #[derive(Clone)] pub enum PredicateClause { Fact(Term), Rule(Rule) } 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()), } } pub fn arity(&self) -> usize { match self { &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()), } } } #[derive(Clone)] pub enum Declaration { Dynamic(ClauseName, usize), // name, arity Hook(CompileTimeHook, PredicateClause, VecDeque), Module(ModuleDecl), NonCountedBacktracking(ClauseName, usize), // name, arity Op(OpDecl), UseModule(ClauseName), UseQualifiedModule(ClauseName, Vec) } impl Declaration { #[inline] pub fn is_module_decl(&self) -> bool { if let &Declaration::Module(_) = self { true } else { false } } } #[derive(Clone)] pub struct OpDecl(pub usize, pub Specifier, pub ClauseName); impl OpDecl { #[inline] pub fn name(&self) -> ClauseName { self.2.clone() } pub fn arity(&self) -> usize { let spec = self.1; if (spec | XFX != 0) || (spec | XFY != 0) || (spec | YFX != 0) { 2 } else { 1 } } pub fn submit(&self, module: ClauseName, op_dir: &mut OpDir) -> Result<(), SessionError> { let (prec, spec, name) = (self.0, self.1, self.2.clone()); if is_infix!(spec) { match op_dir.get(&(name.clone(), Fixity::Post)) { Some(_) => return Err(SessionError::OpIsInfixAndPostFix), _ => {} }; } if is_postfix!(spec) { match op_dir.get(&(name.clone(), Fixity::In)) { Some(_) => return Err(SessionError::OpIsInfixAndPostFix), _ => {} }; } if prec > 0 { match spec { XFY | XFX | YFX => op_dir.insert((name.clone(), Fixity::In), (spec, prec, module.clone())), XF | YF => op_dir.insert((name.clone(), Fixity::Post), (spec, prec, module.clone())), FX | FY => op_dir.insert((name.clone(), Fixity::Pre), (spec, prec, module.clone())), _ => None }; } else { op_dir.remove(&(name.clone(), Fixity::Pre)); op_dir.remove(&(name.clone(), Fixity::In)); op_dir.remove(&(name.clone(), Fixity::Post)); } Ok(()) } } pub type ModuleDir = HashMap; #[derive(Clone)] pub struct ModuleDecl { pub name: ClauseName, pub exports: Vec } pub struct Module { pub atom_tbl: TabledData, pub module_decl: ModuleDecl, pub code_dir: CodeDir, pub op_dir: OpDir, pub term_expansions: (Predicate, VecDeque), pub goal_expansions: (Predicate, VecDeque), pub user_term_expansions: (Predicate, VecDeque), // term expansions inherited from the user scope. pub user_goal_expansions: (Predicate, VecDeque), // same for goal_expansions. pub inserted_expansions: bool // has the module been successfully inserted into toplevel?? }