From fb0b7085b494f5ca52a5c6983b7b77823a1a0b0b Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sat, 6 Apr 2019 17:44:04 -0600 Subject: [PATCH] fix more conformity errors --- Cargo.toml | 4 +- src/prolog/clause_types.rs | 6 ++ src/prolog/heap_print.rs | 138 ++++++++++++++++----------- src/prolog/lib/builtins.pl | 30 ++++-- src/prolog/machine/machine_errors.rs | 4 +- src/prolog/machine/system_calls.rs | 69 ++++++++++++-- src/prolog/read.rs | 12 +-- 7 files changed, 182 insertions(+), 81 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 352b8bbb..dcc362f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "scryer-prolog" -version = "0.8.45" +version = "0.8.46" authors = ["Mark Thom "] repository = "https://github.com/mthom/scryer-prolog" description = "A modern Prolog implementation written mostly in Rust." @@ -14,7 +14,7 @@ cfg-if = "0.1.7" downcast = "0.10.0" num = "0.2" ordered-float = "0.5.0" -prolog_parser = "0.8.16" +prolog_parser = "0.8.17" readline_rs_compat = { version = "0.1.7", optional = true } ref_thread_local = "0.0.0" diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index 520d2ecb..7eb0a03c 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -164,6 +164,7 @@ pub enum SystemClauseType { AtomLength, ModuleAssertDynamicPredicateToFront, ModuleAssertDynamicPredicateToBack, + CharCode, CheckCutPoint, CopyToLiftedHeap, DeleteAttribute, @@ -174,6 +175,7 @@ pub enum SystemClauseType { ExpandGoal, ExpandTerm, FetchGlobalVar, + GetChar, TruncateIfNoLiftedHeapGrowthDiff, TruncateIfNoLiftedHeapGrowth, GetAttributedVariableList, @@ -242,6 +244,7 @@ impl SystemClauseType { &SystemClauseType::AtomLength => clause_name!("$atom_length"), &SystemClauseType::ModuleAssertDynamicPredicateToFront => clause_name!("$module_asserta"), &SystemClauseType::ModuleAssertDynamicPredicateToBack => clause_name!("$module_assertz"), + &SystemClauseType::CharCode => clause_name!("char_code"), &SystemClauseType::CheckCutPoint => clause_name!("$check_cp"), &SystemClauseType::CopyToLiftedHeap => clause_name!("$copy_to_lh"), &SystemClauseType::DeleteAttribute => clause_name!("$del_attr_non_head"), @@ -252,6 +255,7 @@ impl SystemClauseType { &SystemClauseType::ExpandTerm => clause_name!("$expand_term"), &SystemClauseType::ExpandGoal => clause_name!("$expand_goal"), &SystemClauseType::FetchGlobalVar => clause_name!("$fetch_global_var"), + &SystemClauseType::GetChar => clause_name!("$get_char"), &SystemClauseType::TruncateIfNoLiftedHeapGrowth => clause_name!("$truncate_if_no_lh_growth"), &SystemClauseType::TruncateIfNoLiftedHeapGrowthDiff => clause_name!("$truncate_if_no_lh_growth_diff"), &SystemClauseType::GetAttributedVariableList => clause_name!("$get_attr_list"), @@ -320,6 +324,7 @@ impl SystemClauseType { ("$module_assertz", 5) => Some(SystemClauseType::ModuleAssertDynamicPredicateToBack), ("$asserta", 4) => Some(SystemClauseType::AssertDynamicPredicateToFront), ("$assertz", 4) => Some(SystemClauseType::AssertDynamicPredicateToBack), + ("$char_code", 2) => Some(SystemClauseType::CharCode), ("$check_cp", 1) => Some(SystemClauseType::CheckCutPoint), ("$copy_to_lh", 2) => Some(SystemClauseType::CopyToLiftedHeap), ("$del_attr_non_head", 1) => Some(SystemClauseType::DeleteAttribute), @@ -334,6 +339,7 @@ impl SystemClauseType { ("$expand_term", 2) => Some(SystemClauseType::ExpandTerm), ("$expand_goal", 2) => Some(SystemClauseType::ExpandGoal), ("$fetch_global_var", 2) => Some(SystemClauseType::FetchGlobalVar), + ("$get_char", 1) => Some(SystemClauseType::GetChar), ("$truncate_if_no_lh_growth", 1) => Some(SystemClauseType::TruncateIfNoLiftedHeapGrowth), ("$truncate_if_no_lh_growth_diff", 2) => Some(SystemClauseType::TruncateIfNoLiftedHeapGrowthDiff), ("$get_attr_list", 2) => Some(SystemClauseType::GetAttributedVariableList), diff --git a/src/prolog/heap_print.rs b/src/prolog/heap_print.rs index 50eafeaa..632a8fdb 100644 --- a/src/prolog/heap_print.rs +++ b/src/prolog/heap_print.rs @@ -42,7 +42,7 @@ impl DirectedOp { fn needs_bracketing(child_spec: &SharedOpDesc, op: &DirectedOp) -> bool { match op { - &DirectedOp::Left(ref name, ref cell) => { + &DirectedOp::Left(ref name, ref cell) => { let (priority, spec) = cell.get(); if name.as_str() == "-" { @@ -51,15 +51,21 @@ fn needs_bracketing(child_spec: &SharedOpDesc, op: &DirectedOp) -> bool return true; } } - + let is_strict_right = is_yfx!(spec) || is_xfx!(spec) || is_fx!(spec); child_spec.prec() > priority || (child_spec.prec() == priority && is_strict_right) }, &DirectedOp::Right(_, ref cell) => { - let (priority, spec) = cell.get(); + let (priority, spec) = cell.get(); let is_strict_left = is_xfx!(spec) || is_xfy!(spec) || is_xf!(spec); - - child_spec.prec() > priority || (child_spec.prec() == priority && is_strict_left) + + if child_spec.prec() > priority || (child_spec.prec() == priority && is_strict_left) { + true + } else if (is_postfix!(spec) || is_infix!(spec)) && !is_postfix!(child_spec.assoc()) { + child_spec.prec() == priority + } else { + false + } } } } @@ -103,6 +109,20 @@ impl<'a> HCPreOrderIterator<'a> { } } +fn char_to_string(c: char) -> String { + match c { + '\n' => "\\n".to_string(), + '\r' => "\\r".to_string(), + '\t' => "\\t".to_string(), + '\u{0b}' => "\\v".to_string(), // UTF-8 vertical tab + '\u{0c}' => "\\f".to_string(), // UTF-8 form feed + '\u{08}' => "\\b".to_string(), // UTF-8 backspace + '\u{07}' => "\\a".to_string(), // UTF-8 alert + '\x20' ... '\x7e' => c.to_string(), + _ => format!("\\x{:x}\\", c as u32) + } +} + #[derive(Clone)] pub enum TokenOrRedirect { Atom(ClauseName), @@ -250,12 +270,6 @@ pub struct HCPrinter<'a, Outputter> { macro_rules! push_space_if_amb { ($self:expr, $atom:expr, $action:block) => ( if $self.ambiguity_check($atom) { - if $self.last_item_idx > 1 { - if !$self.outputter.range(0 .. $self.last_item_idx).ends_with(" ") { - $self.outputter.insert($self.last_item_idx, ' '); - } - } - $self.outputter.push_char(' '); $action; } else { @@ -277,8 +291,10 @@ fn requires_space(atom: &str, op: &str) -> bool { alpha_numeric_char!(oc) } else if sign_char!(ac) { sign_char!(oc) || decimal_digit_char!(oc) + } else if single_quote_char!(ac) { + single_quote_char!(oc) } else if ac == '0' { - !non_quoted_token(op.chars()) + oc == 'b' || oc == 'x' || oc == 'o' || !non_quoted_token(op.chars()) } else { false } @@ -432,7 +448,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> fn format_negated_operand(&mut self, spec: SharedOpDesc) { let op = DirectedOp::Left(clause_name!("-"), spec); - + self.state_stack.push(TokenOrRedirect::CompositeRedirect(op)); self.state_stack.push(TokenOrRedirect::Space); self.state_stack.push(TokenOrRedirect::Atom(clause_name!("-"))); @@ -465,8 +481,8 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> if self.format_numbered_vars(iter) { return; } - } - + } + if let Some(spec) = ct.spec() { if "." == ct.name().as_str() && is_infix!(spec.assoc()) { if !self.ignore_ops { @@ -474,7 +490,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> return; } } - + if !self.ignore_ops && spec.prec() > 0 { return self.enqueue_op(ct, spec); } @@ -533,7 +549,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> if let Some(offset_str) = self.offset_as_string(addr) { push_space_if_amb!(self, &offset_str, { - self.append_str(offset_str.as_str()); + self.append_str(&offset_str); }); } @@ -555,53 +571,47 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> } fn print_atom(&mut self, atom: &ClauseName) { - push_space_if_amb!(self, atom.as_str(), { - self.print_op_addendum(atom.as_str()); + let result = self.print_op_addendum(atom.as_str()); + + push_space_if_amb!(self, result.as_str(), { + self.append_str(&result); }); } - fn print_op_addendum(&mut self, atom: &str) { + fn print_op_addendum(&mut self, atom: &str) -> String { if !self.quoted || non_quoted_token(atom.chars()) { - self.append_str(atom); + atom.to_string() } else if atom == "''" { - self.append_str("''"); + "''".to_string() } else { + let mut result = String::new(); + if self.quoted { - self.push_char('\''); + result.push('\''); } for c in atom.chars() { - self.print_char(c); + result += &char_to_string(c); } if self.quoted { - self.push_char('\''); + result.push('\''); } + + result } } fn print_op(&mut self, atom: &str) { - push_space_if_amb!(self, atom, { - if atom == "," { - self.push_char(','); - } else { - self.print_op_addendum(atom); - } - }); - } - - fn print_char(&mut self, c: char) { - match c { - '\n' => self.append_str("\\n"), - '\r' => self.append_str("\\r"), - '\t' => self.append_str("\\t"), - '\u{0b}' => self.append_str("\\v"), // UTF-8 vertical tab - '\u{0c}' => self.append_str("\\f"), // UTF-8 form feed - '\u{08}' => self.append_str("\\b"), // UTF-8 backspace - '\u{07}' => self.append_str("\\a"), // UTF-8 alert - '\x20' ... '\x7e' => self.push_char(c), - _ => self.append_str(&format!("\\x{:x}\\", c as u32)) + let result = if atom == "," { + ",".to_string() + } else { + self.print_op_addendum(atom) }; + + push_space_if_amb!(self, &result, { + self.append_str(&result); + }); } fn print_number(&mut self, n: Number, op: &Option) { @@ -645,37 +655,49 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> fn print_constant(&mut self, c: Constant, op: &Option) { match c { - Constant::Atom(ref atom, Some(_)) => { + Constant::Atom(ref atom, Some(ref spec)) if spec.prec() > 0 => { if let Some(ref op) = op { if self.outputter.ends_with(&format!(" {}", op.as_str())) { self.push_char(' '); } - + self.push_char('('); } - + self.print_atom(atom); if op.is_some() { self.push_char(')'); } }, - Constant::Atom(ref atom, None) => + Constant::Atom(ref atom, _) => push_space_if_amb!(self, atom.as_str(), { self.print_atom(atom); }), - Constant::Char(c) if non_quoted_token(once(c)) => - self.print_char(c), - Constant::Char(c) => + Constant::Char(c) if non_quoted_token(once(c)) => { + let c = char_to_string(c); + + push_space_if_amb!(self, &c, { + self.append_str(c.as_str()); + }); + }, + Constant::Char(c) => { + let mut result = String::new(); + if self.quoted { - self.push_char('\''); - self.print_char(c); - self.push_char('\''); + result.push('\''); + result += &char_to_string(c); + result.push('\''); } else { - self.print_char(c); - }, + result += &char_to_string(c); + } + + push_space_if_amb!(self, &result, { + self.append_str(result.as_str()); + }); + }, Constant::CharCode(c) => - self.append_str(&format!("{}", c)), + self.append_str(&format!("{}", c)), Constant::EmptyList => self.append_str("[]"), Constant::Number(n) => diff --git a/src/prolog/lib/builtins.pl b/src/prolog/lib/builtins.pl index e86e305d..6e87ff66 100644 --- a/src/prolog/lib/builtins.pl +++ b/src/prolog/lib/builtins.pl @@ -9,12 +9,12 @@ abolish/1, asserta/1, assertz/1, atom_chars/2, atom_codes/2, atom_length/2, bagof/3, bb_b_put/2, bb_get/2, bb_put/2, call_cleanup/2, call_with_inference_limit/3, catch/3, - clause/2, current_predicate/1, current_op/3, + char_code/2, clause/2, current_predicate/1, current_op/3, current_prolog_flag/2, expand_goal/2, expand_term/2, - findall/3, findall/4, halt/0, once/1, op/3, repeat/0, - retract/1, set_prolog_flag/2, setof/3, setup_call_cleanup/3, - term_variables/2, throw/1, true/0, false/0, write/1, - write_canonical/1, writeq/1, write_term/2]). + findall/3, findall/4, get_char/1, halt/0, once/1, op/3, + repeat/0, retract/1, set_prolog_flag/2, setof/3, + setup_call_cleanup/3, term_variables/2, throw/1, true/0, + false/0, write/1, write_canonical/1, writeq/1, write_term/2]). /* this is an implementation specific declarative operator used to implement call_with_inference_limit/3 and setup_call_cleanup/3. switches to the default trust_me and retry_me_else. Indexing choice @@ -816,7 +816,7 @@ no_var_in_list([]). no_var_in_list([X|Xs]) :- var(X), !, '$fail'. no_var_in_list([_|Xs]) :- no_var_in_list(Xs). -atom_chars(Atom, List) :- +atom_chars(Atom, List) :- ( var(Atom) -> ( var(List) -> throw(error(instantiation_error, atom_chars/2)) ; can_be_list(List, atom_chars/3), no_var_in_list(List) -> '$atom_chars'(Atom, List) @@ -825,7 +825,7 @@ atom_chars(Atom, List) :- ; throw(error(type_error(atom, Atom), atom_chars/2)) ). -atom_codes(Atom, List) :- +atom_codes(Atom, List) :- ( var(Atom) -> ( var(List) -> throw(error(instantiation_error, atom_codes/2)) ; can_be_list(List, atom_codes/3), no_var_in_list(List) -> '$atom_codes'(Atom, List) @@ -833,3 +833,19 @@ atom_codes(Atom, List) :- ; atom(Atom) -> '$atom_codes'(Atom, List) ; throw(error(type_error(atom, Atom), atom_codes/2)) ). + +char_code(Char, Code) :- + ( var(Char) -> + ( var(Code) -> throw(error(instantiation_error, char_code/2)) + ; integer(Code) -> '$char_code'(Char, Code) + ; throw(error(type_error(integer, Code), char_code/2)) + ) + ; atom_length(Char, 1) -> '$char_code'(Char, Code) + ; throw(error(type_error(character, Char), char_code/2)) + ). + +get_char(C) :- + ( var(C) -> '$get_char'(C) + ; atom_length(C, 1) -> '$get_char'(C) + ; throw(error(type_error(in_character, C), get_char/1)) + ). diff --git a/src/prolog/machine/machine_errors.rs b/src/prolog/machine/machine_errors.rs index 59d67086..d69bfef6 100644 --- a/src/prolog/machine/machine_errors.rs +++ b/src/prolog/machine/machine_errors.rs @@ -224,7 +224,7 @@ impl DomainError { // from 7.12.2 f) of 13211-1:1995 #[derive(Clone, Copy)] pub enum RepFlag { -// Character, + Character, CharacterCode, // InCharacterCode, MaxArity, @@ -235,7 +235,7 @@ pub enum RepFlag { impl RepFlag { pub fn as_str(self) -> &'static str { match self { -// RepFlag::Character => "character", + RepFlag::Character => "character", RepFlag::CharacterCode => "character_code", // RepFlag::InCharacterCode => "in_character_code", RepFlag::MaxArity => "max_arity", diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index f8bbde79..ebf42e07 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -15,7 +15,7 @@ use prolog::num::bigint::{BigInt}; use ref_thread_local::RefThreadLocal; use std::collections::HashSet; -use std::io::{stdout, Write}; +use std::io::{stdout, Read, Write}; use std::iter::once; use std::mem; use std::rc::Rc; @@ -355,14 +355,14 @@ impl MachineState { let a2 = self[temp_v!(2)].clone(); let chars = vec![Addr::Con(Constant::Char('[')), Addr::Con(Constant::Char(']'))]; - + let list_of_chars = Addr::HeapCell(self.heap.to_list(chars.into_iter())); self.unify(a2, list_of_chars); }, ref addr if addr.is_ref() => { let stub = MachineError::functor_stub(clause_name!("atom_chars"), 2); - + match self.try_from_list(temp_v!(2), stub.clone()) { Err(e) => return Err(e), Ok(addrs) => { @@ -402,7 +402,7 @@ impl MachineState { let a2 = self[temp_v!(2)].clone(); self.unify(a2, list_of_codes); - }, + }, Addr::Con(Constant::Atom(name, _)) => { let iter = name.as_str().chars().map(|c| Addr::Con(Constant::CharCode(c as u8))); let list_of_codes = Addr::HeapCell(self.heap.to_list(iter)); @@ -415,7 +415,7 @@ impl MachineState { let a2 = self[temp_v!(2)].clone(); let chars = vec![Addr::Con(Constant::CharCode('[' as u8)), Addr::Con(Constant::CharCode(']' as u8))]; - + let list_of_codes = Addr::HeapCell(self.heap.to_list(chars.into_iter())); self.unify(a2, list_of_codes); @@ -452,12 +452,14 @@ impl MachineState { let atom = match self.store(self.deref(a1)) { Addr::Con(Constant::Atom(name, _)) => name, + Addr::Con(Constant::EmptyList) => clause_name!("[]"), + Addr::Con(Constant::Char(c)) => clause_name!(c.to_string(), indices.atom_tbl), _ => unreachable!() }; let len = Number::Integer(Rc::new(BigInt::from_usize(atom.as_str().len()).unwrap())); let a2 = self[temp_v!(2)].clone(); - + self.unify(a2, Addr::Con(Constant::Number(len))); }, &SystemClauseType::ModuleAssertDynamicPredicateToFront => { @@ -480,6 +482,42 @@ impl MachineState { self.unify(a1, lh_len); }, + &SystemClauseType::CharCode => { + let a1 = self[temp_v!(1)].clone(); + + match self.store(self.deref(a1)) { + Addr::Con(Constant::Atom(name, _)) => { + let c = name.as_str().chars().next().unwrap(); + let a2 = self[temp_v!(2)].clone(); + + self.unify(Addr::Con(Constant::CharCode(c as u8)), a2); + }, + Addr::Con(Constant::Char(c)) => { + let a2 = self[temp_v!(2)].clone(); + self.unify(Addr::Con(Constant::CharCode(c as u8)), a2); + }, + ref addr if addr.is_ref() => { + let a2 = self[temp_v!(2)].clone(); + + match self.store(self.deref(a2)) { + Addr::Con(Constant::CharCode(code)) => + self.unify(Addr::Con(Constant::Char(code as char)), addr.clone()), + Addr::Con(Constant::Number(Number::Integer(n))) => + if let Some(c) = n.to_u8() { + self.unify(Addr::Con(Constant::Char(c as char)), addr.clone()); + } else { + let stub = MachineError::functor_stub(clause_name!("char_code"), 2); + let err = MachineError::representation_error(RepFlag::CharacterCode); + let err = self.error_form(err, stub); + + return Err(err); + }, + _ => self.fail = true + }; + }, + _ => unreachable!() + }; + }, &SystemClauseType::CheckCutPoint => { let addr = self.store(self.deref(self[temp_v!(1)].clone())); @@ -503,6 +541,25 @@ impl MachineState { None => self.fail = true }; }, + &SystemClauseType::GetChar => { + let c = std::io::stdin() + .bytes() + .next() + .and_then(|result| result.ok()); + + let a1 = self[temp_v!(1)].clone(); + + match c { + Some(c) => self.unify(Addr::Con(Constant::Char(c as char)), a1), + None => { + let stub = MachineError::functor_stub(clause_name!("get_char"), 1); + let err = MachineError::representation_error(RepFlag::Character); + let err = self.error_form(err, stub); + + return Err(err); + } + } + }, &SystemClauseType::GetModuleClause => { let module = self[temp_v!(3)].clone(); let head = self[temp_v!(1)].clone(); diff --git a/src/prolog/read.rs b/src/prolog/read.rs index 17d030e0..15064868 100644 --- a/src/prolog/read.rs +++ b/src/prolog/read.rs @@ -40,7 +40,7 @@ pub mod readline Single, Multi } - + static mut LINE_MODE: LineMode = LineMode::Single; static mut END_OF_LINE: bool = false; @@ -117,7 +117,7 @@ pub mod readline if let LineMode::Single = LINE_MODE { insert_text_rl("?- "); } - + 0 } @@ -140,7 +140,7 @@ pub mod readline pub mod readline { use std::io::{BufRead, Read, stdin, stdout, Write}; - + pub fn read_batch(_: &str) -> Result, ::SessionError> { let mut buf = vec![]; @@ -159,14 +159,14 @@ pub mod readline let stdin = stdin(); let stdin = stdin.lock(); - + let mut buf = "?- ".to_string(); - + for line in stdin.lines() { match line { Ok(line) => { buf += &line; - + if line.trim().ends_with(".") { break; } -- 2.54.0