From: Mark Thom Date: Sun, 24 Dec 2017 06:23:30 +0000 (-0700) Subject: add beginning support for ',', ';', '->' X-Git-Tag: v0.8.110~649 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=ca16768f7e08a537013a21a224d0cc776ca8f821;p=scryer-prolog.git add beginning support for ',', ';', '->' --- diff --git a/Cargo.toml b/Cargo.toml index 6a8c39d1..7d3168db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rusty-wam" -version = "0.7.3" +version = "0.7.4" authors = ["Mark Thom"] [dependencies] diff --git a/README.md b/README.md index 483c87f1..5e81a895 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Extend rusty-wam to include the following, among other features: * Built-in and user-defined operators of all fixities, with custom associativity and precedence (_done_). * Bignum, rational number and floating point arithmetic (_in progress_). -* Built-in control operators (`,`, `;`, `->`, etc.). +* Built-in control operators (`,`, `;`, `->`, etc.) (_in progress_). * Built-in predicates for list processing and top-level declarative control (`setup_call_control/3`, `call_with_inference_limit/3`, etc.) diff --git a/src/prolog/ast.rs b/src/prolog/ast.rs index d8320d5a..ee86c1b3 100644 --- a/src/prolog/ast.rs +++ b/src/prolog/ast.rs @@ -275,6 +275,12 @@ pub enum Constant { EmptyList } +impl<'a> From<&'a str> for Constant { + fn from(input: &str) -> Constant { + Constant::Atom(String::from(input)) + } +} + impl fmt::Display for Constant { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { @@ -416,6 +422,7 @@ pub enum ChoiceInstruction { TryMeElse(usize) } +#[derive(Clone, Copy)] pub enum Terminal { Terminal, Non } @@ -715,12 +722,15 @@ pub enum BuiltInInstruction { Fail, GetBall, GetCurrentBlock, + GetCutPoint, InstallNewBlock, InternalCallN, IsAtomic(RegType), IsVar(RegType), ResetBlock, SetBall, + SetNeckCutPoint(Terminal), + SetNonNeckCutPoint(Terminal), Succeed, Unify, UnwindStack diff --git a/src/prolog/builtins.rs b/src/prolog/builtins.rs index 7c074e81..3dce6392 100644 --- a/src/prolog/builtins.rs +++ b/src/prolog/builtins.rs @@ -104,7 +104,90 @@ fn get_builtins() -> Code { proceed!(), fact![get_value!(temp_v!(1), 2)], // =/2, 73. proceed!(), - proceed!() // true/0, 75. + proceed!(), // true/0, 75. + try_me_else!(2), // ';'/2, 76. + execute_n!(1), + trust_me!(), + query![put_value!(temp_v!(2), 1)], + execute_n!(1), + allocate!(3), // ','/2, 81. + fact![get_var_in_fact!(perm_v!(2), 1), + get_var_in_fact!(perm_v!(1), 2)], + query![put_var!(perm_v!(3), 1)], + get_cp!(), + query![put_value!(perm_v!(2), 1), + put_value!(perm_v!(1), 2), + put_unsafe_value!(3, 3)], + deallocate!(), + goto!(88, 3), + try_me_else!(25), // ','/3, 88. + switch_on_term!(4, 0, 0, 0), + indexed_try!(4), + retry!(11), + trust!(14), + try_me_else!(8), + allocate!(3), + fact![get_constant!(Constant::from("!"), temp_v!(1)), + get_structure!(String::from(","), 2, temp_v!(2)), + unify_variable!(perm_v!(2)), + unify_variable!(perm_v!(1)), + get_var_in_fact!(perm_v!(3), 3)], + query![put_value!(perm_v!(3), 1)], + set_non_neck_cp!(non_terminal!()), + query![put_unsafe_value!(2, 1), + put_unsafe_value!(1, 2), + put_value!(perm_v!(3), 3)], + deallocate!(), + goto!(88, 3), + retry_me_else!(4), + fact![get_constant!(Constant::from("!"), temp_v!(1)), + get_constant!(Constant::from("!"), temp_v!(2))], + query![put_value!(temp_v!(3), 1)], + set_neck_cp!(terminal!()), + trust_me!(), + allocate!(1), + fact![get_constant!(Constant::from("!"), temp_v!(1)), + get_var_in_fact!(perm_v!(1), 2)], + query![put_value!(temp_v!(3), 1)], + set_non_neck_cp!(non_terminal!()), + query![put_value!(perm_v!(1), 1)], + deallocate!(), + execute_n!(1), + retry_me_else!(7), + allocate!(1), + fact![get_constant!(Constant::from("!"), temp_v!(2)), + get_var_in_fact!(perm_v!(1), 3)], + call_n!(1), + query![put_value!(perm_v!(1), 1)], + deallocate!(), + set_non_neck_cp!(terminal!()), + retry_me_else!(7), + allocate!(3), + fact![get_structure!(String::from(","), 2, temp_v!(2)), + unify_variable!(perm_v!(2)), + unify_variable!(perm_v!(1)), + get_var_in_fact!(perm_v!(3), 3)], + call_n!(1), + query![put_unsafe_value!(2, 1), + put_unsafe_value!(1, 2), + put_value!(perm_v!(3), 3)], + deallocate!(), + goto!(88, 3), + trust_me!(), + allocate!(1), + fact![get_var_in_fact!(perm_v!(1), 2)], + call_n!(1), + query![put_value!(perm_v!(1), 1)], + deallocate!(), + execute_n!(1), + allocate!(2), // (->)/2, 134. + get_level!(), + fact![get_var_in_fact!(perm_v!(2), 2)], + call_n!(1), + cut!(non_terminal!()), + query![put_value!(perm_v!(2), 1)], + deallocate!(), + execute_n!(1) ] } @@ -148,6 +231,10 @@ pub fn build_code_dir() -> (Code, CodeDir, OpDir) op_dir.insert((String::from("=:="), Fixity::In), (XFX, 700)); op_dir.insert((String::from(">="), Fixity::In), (XFX, 700)); op_dir.insert((String::from("=<"), Fixity::In), (XFX, 700)); + + // control operators. + op_dir.insert((String::from(";"), Fixity::In), (XFY, 1100)); + op_dir.insert((String::from("->"), Fixity::In), (XFY, 1050)); // there are 63 registers in the VM, so call/N is defined for all 0 <= N <= 62 // (an extra register is needed for the predicate name) @@ -163,7 +250,11 @@ pub fn build_code_dir() -> (Code, CodeDir, OpDir) code_dir.insert((String::from("catch"), 3), (PredicateKeyType::BuiltIn, 5)); code_dir.insert((String::from("throw"), 1), (PredicateKeyType::BuiltIn, 59)); code_dir.insert((String::from("="), 2), (PredicateKeyType::BuiltIn, 73)); - code_dir.insert((String::from("true"), 0), (PredicateKeyType::BuiltIn, 75)); + code_dir.insert((String::from("true"), 0), (PredicateKeyType::BuiltIn, 75)); + code_dir.insert((String::from(";"), 2), (PredicateKeyType::BuiltIn, 76)); + code_dir.insert((String::from(","), 2), (PredicateKeyType::BuiltIn, 81)); + code_dir.insert((String::from("->"), 2), (PredicateKeyType::BuiltIn, 134)); + (builtin_code, code_dir, op_dir) } diff --git a/src/prolog/codegen.rs b/src/prolog/codegen.rs index 213141e6..c21a1bc0 100644 --- a/src/prolog/codegen.rs +++ b/src/prolog/codegen.rs @@ -65,8 +65,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker> self.marker.take_bindings() } - fn update_var_count(&mut self, iter: Iter) - where Iter: Iterator> + fn update_var_count>>(&mut self, iter: Iter) { for term in iter { if let TermRef::Var(_, _, var) = term { @@ -96,7 +95,11 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker> self.marker.reset_arg(arity); self.marker.mark_var(name, Level::Shallow, vr, term_loc, &mut target); - code.push(Line::Query(target)); + + if !target.is_empty() { + code.push(Line::Query(target)); + } + vr.get().norm() } } @@ -233,13 +236,6 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker> fn add_conditional_call_inlined(_: &InlinedQueryTerm, code: &mut Code) { code.push(proceed!()); - - /* - match term { - &InlinedQueryTerm::IsAtomic(_) | &InlinedQueryTerm::IsVar(_) => - code.push(proceed!()) - }; - */ } fn add_conditional_call(code: &mut Code, qt: &QueryTerm, pvs: usize) @@ -340,7 +336,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker> Ok(()) } - fn call_arith_eval(&self, term: &'a Term, target_int: usize) -> Result + fn call_arith_eval(&self, term: &'a Term, target_int: usize) + -> Result { let mut evaluator = ArithmeticEvaluator::new(self.marker.bindings(), target_int); evaluator.eval(term) @@ -421,7 +418,12 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker> self.marker.reset_arg(term.arity()); let iter = term.post_order_iter(); - code.push(Line::Query(self.compile_target(iter, term_loc, is_exposed))); + let query = self.compile_target(iter, term_loc, is_exposed); + + if !query.is_empty() { + code.push(Line::Query(query)); + } + Self::add_conditional_call(code, term, conjunct_info.perm_vars()); }, _ => { @@ -479,7 +481,11 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker> if let &QueryTerm::Term(ref term) = p0 { if let &Term::Clause(_, _, _) = term { let iter = FactInstruction::iter(term); - code.push(Line::Fact(self.compile_target(iter, GenContext::Head, false))); + let fact = self.compile_target(iter, GenContext::Head, false); + + if !fact.is_empty() { + code.push(Line::Fact(fact)); + } } let iter = ChunkedIterator::from_rule_body(p1, clauses); @@ -550,7 +556,10 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker> let mut compiled_fact = self.compile_target(iter, GenContext::Head, false); self.mark_unsafe_fact_vars(&mut compiled_fact); - code.push(Line::Fact(compiled_fact)); + + if !compiled_fact.is_empty() { + code.push(Line::Fact(compiled_fact)); + } } code.push(proceed!()); @@ -567,9 +576,11 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker> self.marker.reset_arg(term.arity()); let iter = term.post_order_iter(); - let compiled_query = Line::Query(self.compile_target(iter, term_loc, is_exposed)); + let compiled_query = self.compile_target(iter, term_loc, is_exposed); - code.push(compiled_query); + if !compiled_query.is_empty() { + code.push(Line::Query(compiled_query)); + } Self::add_conditional_call(code, term, index); } diff --git a/src/prolog/io.rs b/src/prolog/io.rs index 4bb6a5a8..d63b700d 100644 --- a/src/prolog/io.rs +++ b/src/prolog/io.rs @@ -141,6 +141,16 @@ impl fmt::Display for IndexedChoiceInstruction { } } +impl fmt::Display for Terminal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + &Terminal::Terminal => + write!(f, "terminal"), + &Terminal::Non => + write!(f, "non_terminal") + } + } +} impl fmt::Display for BuiltInInstruction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -159,6 +169,8 @@ impl fmt::Display for BuiltInInstruction { write!(f, "get_ball X1"), &BuiltInInstruction::GetCurrentBlock => write!(f, "get_current_block X1"), + &BuiltInInstruction::GetCutPoint => + write!(f, "get_cp"), &BuiltInInstruction::InstallNewBlock => write!(f, "install_new_block"), &BuiltInInstruction::InternalCallN => @@ -171,6 +183,10 @@ impl fmt::Display for BuiltInInstruction { write!(f, "reset_block"), &BuiltInInstruction::SetBall => write!(f, "set_ball"), + &BuiltInInstruction::SetNeckCutPoint(terminal) => + write!(f, "set_neck_cp {}", terminal), + &BuiltInInstruction::SetNonNeckCutPoint(terminal) => + write!(f, "set_non_neck_cp {}", terminal), &BuiltInInstruction::Succeed => write!(f, "true"), &BuiltInInstruction::UnwindStack => diff --git a/src/prolog/machine.rs b/src/prolog/machine.rs index 7dcaba6d..047a05f1 100644 --- a/src/prolog/machine.rs +++ b/src/prolog/machine.rs @@ -1621,7 +1621,14 @@ impl MachineState { self.write_constant_to_var(addr, &c); self.p += 1; - }, + }, + &BuiltInInstruction::GetCutPoint => { + let c = Constant::Usize(self.b); + let addr = self[temp_v!(1)].clone(); + + self.write_constant_to_var(addr, &c); + self.p += 1; + }, &BuiltInInstruction::EraseBall => { self.ball.0 = 0; self.ball.1.truncate(0); @@ -1659,6 +1666,28 @@ impl MachineState { self.p += 1; }, + &BuiltInInstruction::SetNeckCutPoint(terminal) => { + let nb = self.store(self.deref(self[temp_v!(1)].clone())); + + match nb { + Addr::Con(Constant::Usize(nb)) => { + self.b = nb; + self.neck_cut(terminal); + }, + _ => self.fail = true + }; + }, + &BuiltInInstruction::SetNonNeckCutPoint(terminal) => { + let nb = self.store(self.deref(self[temp_v!(1)].clone())); + + match nb { + Addr::Con(Constant::Usize(nb)) => { + self.b = nb; + self.non_neck_cut(terminal); + }, + _ => self.fail = true + }; + }, &BuiltInInstruction::CleanUpBlock => { let nb = self.store(self.deref(self[temp_v!(1)].clone())); @@ -1997,24 +2026,45 @@ impl MachineState { } } - fn execute_cut_instr(&mut self, instr: &CutInstruction) { - match instr { - &CutInstruction::Cut(ref term) => { - let b = self.b; - let e = self.e; - let b0 = self.and_stack[e].b0; // STACK[E+2+1] + fn non_neck_cut(&mut self, term: Terminal) + { + let b = self.b; + let e = self.e; + let b0 = self.and_stack[e].b0; // STACK[E+2+1] - if b > b0 { - self.b = b0; - self.tidy_trail(); - } + if b > b0 { + self.b = b0; + self.tidy_trail(); + } - if let &Terminal::Terminal = term { - self.p = self.cp; - } else { - self.p += 1; - } - }, + if let Terminal::Terminal = term { + self.p = self.cp; + } else { + self.p += 1; + } + } + + fn neck_cut(&mut self, term: Terminal) + { + let b = self.b; + let b0 = self.b0; + + if b > b0 { + self.b = b0; + self.tidy_trail(); + } + + if let Terminal::Terminal = term { + self.p = self.cp; + } else { + self.p += 1; + } + } + + fn execute_cut_instr(&mut self, instr: &CutInstruction) { + match instr { + &CutInstruction::Cut(term) => + self.non_neck_cut(term), &CutInstruction::GetLevel => { let b0 = self.b0; let e = self.e; @@ -2022,21 +2072,8 @@ impl MachineState { self.and_stack[e].b0 = b0; self.p += 1; }, - &CutInstruction::NeckCut(ref term) => { - let b = self.b; - let b0 = self.b0; - - if b > b0 { - self.b = b0; - self.tidy_trail(); - } - - if let &Terminal::Terminal = term { - self.p = self.cp; - } else { - self.p += 1; - } - } + &CutInstruction::NeckCut(term) => + self.neck_cut(term) } } diff --git a/src/prolog/macros.rs b/src/prolog/macros.rs index 130ac3c4..c31e5f59 100644 --- a/src/prolog/macros.rs +++ b/src/prolog/macros.rs @@ -123,6 +123,12 @@ macro_rules! try_me_else { ) } +macro_rules! retry_me_else { + ($o:expr) => ( + Line::Choice(ChoiceInstruction::RetryMeElse($o)) + ) +} + macro_rules! is_atomic { ($reg:expr) => ( Line::BuiltIn(BuiltInInstruction::IsAtomic($reg)) @@ -159,6 +165,12 @@ macro_rules! proceed { ) } +macro_rules! terminal { + () => ( + Terminal::Terminal + ) +} + macro_rules! non_terminal { () => ( Terminal::Non @@ -260,3 +272,63 @@ macro_rules! get_level { Line::Cut(CutInstruction::GetLevel) ) } + +macro_rules! switch_on_term { + ($v:expr, $c:expr, $l:expr, $s:expr) => ( + Line::Indexing(IndexingInstruction::SwitchOnTerm($v, $c, $l, $s)) + ) +} + +macro_rules! indexed_try { + ($i:expr) => ( + Line::IndexedChoice(IndexedChoiceInstruction::Try($i)) + ) +} + +macro_rules! retry { + ($i:expr) => ( + Line::IndexedChoice(IndexedChoiceInstruction::Retry($i)) + ) +} + +macro_rules! trust { + ($i:expr) => ( + Line::IndexedChoice(IndexedChoiceInstruction::Trust($i)) + ) +} + +macro_rules! get_cp { + () => ( + Line::BuiltIn(BuiltInInstruction::GetCutPoint) + ) +} + +macro_rules! set_neck_cp { + ($term:expr) => ( + Line::BuiltIn(BuiltInInstruction::SetNeckCutPoint($term)) + ) +} + +macro_rules! set_non_neck_cp { + ($term:expr) => ( + Line::BuiltIn(BuiltInInstruction::SetNonNeckCutPoint($term)) + ) +} + +macro_rules! get_constant { + ($c:expr, $r:expr) => ( + FactInstruction::GetConstant(Level::Shallow, $c, $r) + ) +} + +macro_rules! get_structure { + ($atom:expr, $arity:expr, $r:expr) => ( + FactInstruction::GetStructure(Level::Shallow, $atom, $arity, $r) + ) +} + +macro_rules! unify_variable { + ($r:expr) => ( + FactInstruction::UnifyVariable($r) + ) +}