From 9cdad087ef2b064073faf650fa65912ade038e03 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 3 Jul 2023 11:35:07 -0600 Subject: [PATCH] add double_quotes write option for printing to strings, enable it at toplevel --- build/instructions_template.rs | 4 ++-- src/heap_print.rs | 13 ++++++++--- src/lib/builtins.pl | 40 +++++++++++++++++++--------------- src/lib/charsio.pl | 7 +++--- src/machine/machine_state.rs | 23 ++++++++++++++++++- src/machine/mock_wam.rs | 1 + src/parser/ast.rs | 2 +- src/toplevel.pl | 12 +++++----- 8 files changed, 68 insertions(+), 34 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 7f559b8e..dc88a827 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -472,9 +472,9 @@ enum SystemClauseType { WAMInstructions, #[strum_discriminants(strum(props(Arity = "2", Name = "$inlined_instructions")))] InlinedInstructions, - #[strum_discriminants(strum(props(Arity = "7", Name = "$write_term")))] + #[strum_discriminants(strum(props(Arity = "8", Name = "$write_term")))] WriteTerm, - #[strum_discriminants(strum(props(Arity = "7", Name = "$write_term_to_chars")))] + #[strum_discriminants(strum(props(Arity = "8", Name = "$write_term_to_chars")))] WriteTermToChars, #[strum_discriminants(strum(props(Arity = "1", Name = "$scryer_prolog_version")))] ScryerPrologVersion, diff --git a/src/heap_print.rs b/src/heap_print.rs index 4025339c..4aef1ab1 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -478,6 +478,7 @@ pub struct HCPrinter<'a, Outputter> { iter: StackfulPreOrderHeapIter<'a>, atom_tbl: &'a mut AtomTable, op_dir: &'a OpDir, + flags: MachineFlags, state_stack: Vec, toplevel_spec: Option, last_item_idx: usize, @@ -488,6 +489,7 @@ pub struct HCPrinter<'a, Outputter> { pub ignore_ops: bool, pub print_strings_as_strs: bool, pub max_depth: usize, + pub double_quotes: bool, } macro_rules! push_space_if_amb { @@ -544,6 +546,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { atom_tbl: &'a mut AtomTable, stack: &'a mut Stack, op_dir: &'a OpDir, + flags: MachineFlags, output: Outputter, cell: HeapCellValue, ) -> Self { @@ -552,6 +555,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { iter: stackful_preorder_iter(heap, stack, cell), atom_tbl, op_dir, + flags, state_stack: vec![], toplevel_spec: None, last_item_idx: 0, @@ -562,6 +566,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { var_names: IndexMap::new(), print_strings_as_strs: false, max_depth: 0, + double_quotes: false, } } @@ -1164,9 +1169,11 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let at_cdr = self.outputter.ends_with("|"); - if !at_cdr && !self.ignore_ops && end_cell.is_string_terminator(&self.iter.heap) { - self.remove_list_children(focus.value() as usize); - return self.print_proper_string(focus.value() as usize, max_depth); + if self.double_quotes && self.flags.double_quotes == DoubleQuotes::Chars { + if !at_cdr && !self.ignore_ops && end_cell.is_string_terminator(&self.iter.heap) { + self.remove_list_children(focus.value() as usize); + return self.print_proper_string(focus.value() as usize, max_depth); + } } if self.ignore_ops { diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index 904d2f69..4794c603 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -528,30 +528,34 @@ parse_options_list(Options, Selector, DefaultPairs, OptionValues, Stub) :- parse_write_options(Options, OptionValues, Stub) :- - DefaultOptions = [ignore_ops-false, max_depth-0, numbervars-false, + DefaultOptions = [double_quotes-false, ignore_ops-false, max_depth-0, numbervars-false, quoted-false, variable_names-[]], parse_options_list(Options, builtins:parse_write_options_, DefaultOptions, OptionValues, Stub). + +parse_write_options_(double_quotes(DoubleQuotes), double_quotes-DoubleQuotes) :- + ( nonvar(DoubleQuotes), + lists:member(DoubleQuotes, [true, false]), + ! + ; throw(error(domain_error(write_option, double_quotes(DoubleQuotes)), _)) + ). parse_write_options_(ignore_ops(IgnoreOps), ignore_ops-IgnoreOps) :- ( nonvar(IgnoreOps), lists:member(IgnoreOps, [true, false]), ! - ; - throw(error(domain_error(write_option, ignore_ops(IgnoreOps)), _)) + ; throw(error(domain_error(write_option, ignore_ops(IgnoreOps)), _)) ). parse_write_options_(quoted(Quoted), quoted-Quoted) :- ( nonvar(Quoted), lists:member(Quoted, [true, false]), ! - ; - throw(error(domain_error(write_option, quoted(Quoted)), _)) + ; throw(error(domain_error(write_option, quoted(Quoted)), _)) ). parse_write_options_(numbervars(NumberVars), numbervars-NumberVars) :- ( nonvar(NumberVars), lists:member(NumberVars, [true, false]), ! - ; - throw(error(domain_error(write_option, numbervars(NumberVars)), _)) + ; throw(error(domain_error(write_option, numbervars(NumberVars)), _)) ). parse_write_options_(variable_names(VNNames), variable_names-VNNames) :- must_be_var_names_list(VNNames), @@ -560,8 +564,7 @@ parse_write_options_(max_depth(MaxDepth), max_depth-MaxDepth) :- ( integer(MaxDepth), MaxDepth >= 0, ! - ; - throw(error(domain_error(write_option, max_depth(MaxDepth)), _)) + ; throw(error(domain_error(write_option, max_depth(MaxDepth)), _)) ). parse_write_options_(E, _) :- throw(error(domain_error(write_option, E), _)). @@ -607,11 +610,12 @@ write_term(Term, Options) :- % * `max_depth(+N)` if the term is nested deeper than N, print the reminder as ellipses. % If N = 0 (default), there's no limit. % * `numbervars(+Boolean)` if true, replaces `$VAR(N)` variables with letters, in order. Default is false. -% * `quoted(+Boolean)` if true, strings and atoms that need quotes to be valid Prolog synytax, are quoted. Default is false. +% * `quoted(+Boolean)` if true, strings and atoms that need quotes to be valid Prolog syntax, are quoted. Default is false. % * `variable_names(+List)` assign names to variables in term. List should be a list of terms of format `Name=Var`. +% * `double_quotes(+Boolean)` if true, strings are printed in double quotes rather than with list notation. Default is false. write_term(Stream, Term, Options) :- - parse_write_options(Options, [IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames], write_term/3), - '$write_term'(Stream, Term, IgnoreOps, NumberVars, Quoted, VNNames, MaxDepth). + parse_write_options(Options, [DoubleQuotes, IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames], write_term/3), + '$write_term'(Stream, Term, IgnoreOps, NumberVars, Quoted, VNNames, MaxDepth, DoubleQuotes). %% write(+Term). @@ -619,26 +623,26 @@ write_term(Stream, Term, Options) :- % Write Term to the current output stream using a syntax similar to Prolog write(Term) :- current_output(Stream), - '$write_term'(Stream, Term, false, true, false, [], 0). + '$write_term'(Stream, Term, false, true, false, [], 0, false). %% write(+Stream, +Term). % % Write Term to the stream Stream using a syntax similar to Prolog write(Stream, Term) :- - '$write_term'(Stream, Term, false, true, false, [], 0). + '$write_term'(Stream, Term, false, true, false, [], 0, false). %% write_canonical(+Term). % % Write Term to the current output stream using canonical Prolog syntax. Can be read back as Prolog terms. write_canonical(Term) :- current_output(Stream), - '$write_term'(Stream, Term, true, false, true, [], 0). + '$write_term'(Stream, Term, true, false, true, [], 0, false). %% write_canonical(+Stream, +Term). % % Write Term to the stream Stream using canonical Prolog syntax. Can be read back as Prolog terms. write_canonical(Stream, Term) :- - '$write_term'(Stream, Term, true, false, true, [], 0). + '$write_term'(Stream, Term, true, false, true, [], 0, false). %% writeq(+Term). % @@ -646,14 +650,14 @@ write_canonical(Stream, Term) :- % quoted according to Prolog syntax. writeq(Term) :- current_output(Stream), - '$write_term'(Stream, Term, false, true, true, [], 0). + '$write_term'(Stream, Term, false, true, true, [], 0, false). %% writeq(+Stream, +Term). % % Write Term to the stream Stream using a syntax similar to `write/1` but quoting the atoms that need to be % quoted according to Prolog syntax. writeq(Stream, Term) :- - '$write_term'(Stream, Term, false, true, true, [], 0). + '$write_term'(Stream, Term, false, true, true, [], 0, false). select_rightmost_options([Option-Value | OptionPairs], OptionValues) :- ( pairs:same_key(Option, OptionPairs, OtherValues, _), diff --git a/src/lib/charsio.pl b/src/lib/charsio.pl index 99b5c281..128a6c50 100644 --- a/src/lib/charsio.pl +++ b/src/lib/charsio.pl @@ -206,13 +206,14 @@ read_from_chars(Chars, Term) :- % * `max_depth(+N)` if the term is nested deeper than N, print the reminder as ellipses. % If N = 0 (default), there's no limit. % * `numbervars(+Boolean)` if true, replaces `$VAR(N)` variables with letters, in order. Default is false. -% * `quoted(+Boolean)` if true, strings and atoms that need quotes to be valid Prolog synytax, are quoted. Default is false. +% * `quoted(+Boolean)` if true, strings and atoms that need quotes to be valid Prolog syntax, are quoted. Default is false. % * `variable_names(+List)` assign names to variables in term. List should be a list of terms of format `Name=Var`. +% * `double_quotes(+Boolean)` if true, strings are printed in double quotes rather than with list notation. Default is false. write_term_to_chars(_, Options, _) :- var(Options), instantiation_error(write_term_to_chars/3). write_term_to_chars(Term, Options, Chars) :- builtins:parse_write_options(Options, - [IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames], + [DoubleQuotes, IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames], write_term_to_chars/3), ( nonvar(Chars) -> throw(error(uninstantiation_error(Chars), write_term_to_chars/3)) @@ -221,7 +222,7 @@ write_term_to_chars(Term, Options, Chars) :- ), term_variables(Term, Vars), extend_var_list(Vars, VNNames, NewVarNames, numbervars), - '$write_term_to_chars'(Chars, Term, IgnoreOps, NumberVars, Quoted, NewVarNames, MaxDepth). + '$write_term_to_chars'(Chars, Term, IgnoreOps, NumberVars, Quoted, NewVarNames, MaxDepth, DoubleQuotes). % Encodes Ch character to list of Bytes. char_utf8bytes(Ch, Bytes) :- diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 29702c4a..78b1a7f5 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -666,6 +666,7 @@ impl MachineState { let numbervars = self.store(self.deref(self.registers[4])); let quoted = self.store(self.deref(self.registers[5])); let max_depth = self.store(self.deref(self.registers[7])); + let double_quotes = self.store(self.deref(self.registers[8])); let term_to_be_printed = self.store(self.deref(self.registers[2])); let stub_gen = || functor_stub(atom!("write_term"), 2); @@ -747,7 +748,25 @@ impl MachineState { ); let quoted = read_heap_cell!(quoted, - (HeapCellValueTag::Atom, (name, _arity)) => { + (HeapCellValueTag::Atom, (name, arity)) => { + debug_assert_eq!(arity, 0); + name == atom!("true") + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(self.heap[s]) + .get_name_and_arity(); + + debug_assert_eq!(arity, 0); + name == atom!("true") + } + _ => { + unreachable!() + } + ); + + let double_quotes = read_heap_cell!(double_quotes, + (HeapCellValueTag::Atom, (name, arity)) => { + debug_assert_eq!(arity, 0); name == atom!("true") } (HeapCellValueTag::Str, s) => { @@ -767,6 +786,7 @@ impl MachineState { &mut self.atom_tbl, &mut self.stack, op_dir, + self.flags, PrinterOutputter::new(), term_to_be_printed, ); @@ -774,6 +794,7 @@ impl MachineState { printer.ignore_ops = ignore_ops; printer.numbervars = numbervars; printer.quoted = quoted; + printer.double_quotes = double_quotes; match Number::try_from(max_depth) { Ok(Number::Fixnum(n)) => { diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index 70264ac9..a4aad74c 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -64,6 +64,7 @@ impl MockWAM { &mut self.machine_st.atom_tbl, &mut self.machine_st.stack, &self.op_dir, + self.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(term_write_result.heap_loc), ); diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 31f1a702..272d5b7e 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -313,7 +313,7 @@ impl Default for MachineFlags { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum DoubleQuotes { Atom, Chars, diff --git a/src/toplevel.pl b/src/toplevel.pl index 30de5227..2df6d360 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -231,13 +231,13 @@ write_goal(G, VarList, MaxDepth) :- write(' = '), ( needs_bracketing(Value, =) -> write('('), - write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]), + write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]), write(')') - ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]) + ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]) ) ; G == [] -> write('true') - ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth)]) + ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth), double_quotes(true)]) ). write_last_goal(G, VarList, MaxDepth) :- @@ -250,9 +250,9 @@ write_last_goal(G, VarList, MaxDepth) :- write(' = '), ( needs_bracketing(Value, =) -> write('('), - write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]), + write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]), write(')') - ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]), + ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]), ( trailing_period_is_ambiguous(Value) -> write(' ') ; true @@ -260,7 +260,7 @@ write_last_goal(G, VarList, MaxDepth) :- ) ; G == [] -> write('true') - ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth)]) + ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth), double_quotes(true)]) ). write_eq((G1, G2), VarList, MaxDepth) :- -- 2.54.0