Files
mercury/compiler/mercury_to_mercury.m
Julien Fischer f519e26173 Add builtin 64-bit integer types -- Part 1.
Add the new builtin types: int64 and uint64.

Support for these new types will need to be bootstrapped over several changes.
This is the first such change and does the following:

- Extends the compiler to recognise 'int64' and 'uint64' as builtin types.
- Extends the set of builtin arithmetic, bitwise and relational operators
  to cover the new types.
- Adds the new internal option '--unboxed-int64s' to the compiler; this will be
  used to control whether 64-bit integer types are boxed or not.
- Extends all of the code generators to handle the new types.
- Extends the runtimes to support the new types.
- Adds new modules to the standard library intend to contain basic operations
  on the new types.  (These are currently empty and not documented.)

There are bunch of limitations marks with "XXX INT64"; these will be lifted in
part 2 of this change.  Also, 64-bit integer types are currently always boxed,
again this limitation will be lifted in later changes.

compiler/options.m:
    Add the new option --unboxed-int64s.

compiler/prog_type.m:
compiler/prog_data.m:
compiler/builtin_lib_types.m:
     Recognise int64 and uint64 as builtin types.

compiler/builtin_ops.m:
     Add builtin operations for the new types.

compiler/hlds_data.m:
     Add new tag types for the new types.

compiler/ctgc.selector.m:
compiler/dead_proc_elim.m:
compiler/export.m:
compiler/foreign.m:
compiler/goal_util.m:
compiler/higher_order.m:
compiler/hlds_code_util.m:
compiler/hlds_dependency_graph.m:
compiler/hlds_out_pred.m:
compiler/hlds_out_util.m:
compiler/implementation_defined_literals.m:
compiler/inst_check.m:
compiler/mercury_to_mercury.m:
compiler/mode_util.m:
compiler/module_qual.qualify_items.m:
compiler/opt_debug.m:
compiler/opt_util.m:
compiler/parse_tree_to_term.m:
compiler/parse_type_name.m:
compiler/polymorphism.m:
compiler/prog_out.m:
compiler/prog_util.m:
compiler/rbmm.execution_path.m:
compiler/rtti.m:
compiler/table_gen.m:
compiler/type_util.m:
compiler/typecheck.m:
compiler/unify_gen.m:
compiler/unify_proc.m:
compiler/unused_imports.m:
compiler/xml_documentation.m:
    Conform to the above changes to the parse tree and HLDS.

compiler/c_util.m:
    Support writing out constants of the new types.

compiler/llds.m:
    Add a representation for constants of the new types to the LLDS.

compiler/stack_layout.m:
    Add a new field to the stack layout params that records whether
    64-bit integers are boxed or not.

compiler/call_gen.:m
compiler/code_info.m:
compiler/disj_gen.m:
compiler/dupproc.m:
compiler/exprn_aux.m:
compiler/global_data.m:
compiler/jumpopt.m:
compiler/llds_out_data.m:
compiler/llds_out_instr.m:
compiler/lookup_switch.m:
compiler/mercury_compile_llds_back_end.m:
compiler/prog_rep.m:
compiler/prog_rep_tables.m:
compiler/var_locn.m b/compiler/var_locn.m:
    Support the new types in the LLDS code generator.

compiler/mlds.m:
    Support constants of the new types in the MLDS.

compiler/ml_call_gen.m:
compiler/ml_code_util.m:
compiler/ml_global_data.m:
compiler/ml_rename_classes.m:
compiler/ml_top_gen.m:
compiler/ml_type_gen.m:
compiler/ml_unify_gen.m:
compiler/ml_util.m:
compiler/mlds_to_target_util.m:
compiler/rtti_to_mlds.m:
     Conform to the above changes to the MLDS.

compiler/mlds_to_c.m:
compiler/mlds_to_cs.m:
compiler/mlds_to_java.m:
    Generate the appropriate target code for constants of the new types
    and operations involving them.

compiler/bytecode.m:
compiler/bytecode_gen.m:
    Handle the new types in the bytecode generator; we just abort if we
    encounter them for now.

compiler/elds.m:
compiler/elds_to_erlang.m:
compiler/erl_call_gen.m:
compiler/erl_code_util.m:
compiler/erl_unify_gen.m:
    Handle the new types in the Erlang code generator.

library/private_builtin.m:
    Add placeholders for the builtin unify and compare operations for
    the new types.  Since the bootstrapping compiler will not recognise
    the new types we give them polymorphic arguments.  These can be
    replaced after this change has bootstrapped.

    Update the Java list of TypeCtorRep constants here.

library/int64.m:
library/uint64.m:
    New modules that will eventually contain builtin operations on the new
    types.

library/library.m:
library/MODULES_UNDOC:
    Do not include the above modules in the library documentation for now.

library/construct.m:
library/erlang_rtti_implementation.m:
library/rtti_implementation.m:
library/table_statistics.m:
deep_profiler/program_representation_utils.m:
mdbcomp/program_representation.m:
    Handle the new types.

configure.ac:
runtime/mercury_conf.h.in:
    Define the macro MR_BOXED_INT64S.  For now it is always defined, support for
    unboxed 64-bit integers will be enabled in a later change.

runtime/mercury_dotnet.cs.in:
java/runtime/TypeCtorRep.java:
runtime/mercury_type_info.h:
    Update the list of type_ctor reps.

runtime/mercury.h:
runtime/mercury_int.[ch]:
    Add macros for int64 / uint64 -> MR_Word conversion, boxing and
    unboxing.

    Add functions for hashing 64-bit integer types suitable for use
    with the tabling mechanism.

runtime/mercury_tabling.[ch]:
    Add additional HashTableSlot structs for 64-bit integer types.

    Omit the '%' character from the conversion specifiers we pass via
    the 'key_format' argument to the macros that generate the table lookup
    function.  This is so we can use the C99 exact size integer conversion
    specifiers (e.g. PRIu64 etc.) directly here.

runtime/mercury_hash_lookup_or_add_body.h:
    Add the '%' character that was omitted above to the call to debug_key_msg.

runtime/mercury_memory.h:
     Add new builtin allocation sites for boxed 64-bit integer types.

runtime/mercury_builtin_types.[ch]:
runtime/mercury_builitn_types_proc_layouts.h:
runtime/mercury_construct.c:
runtime/mercury_deconstruct.c:
runtime/mercury_deep_copy_body.h:
runtime/mercury_ml_expand_body.h:
runtime/mercury_table_type_body.h:
runtime/mercury_tabling_macros.h:
runtime/mercury_tabling_preds.h:
runtime/mercury_term_size.c:
runtime/mercury_unify_compare_body.h:
    Add the new builtin types and handle them throughout the runtime.

runtime/Mmakefile:
    Add mercury_int.c to the list of .c files.

doc/reference_manual.texi:
     Add the new types to the list of reserved type names.

     Add the mapping from the new types to their target language types.
     These are commented out for now.
2018-01-12 09:29:24 -05:00

516 lines
18 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1994-2012 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%---------------------------------------------------------------------------%
%
% File: mercury_to_mercury.m.
% Main author: fjh.
%
% This module converts miscellaneous parts of the parse tree structure
% back into Mercury source text.
%
%---------------------------------------------------------------------------%
:- module parse_tree.mercury_to_mercury.
:- interface.
:- import_module libs.globals.
:- import_module mdbcomp.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.parse_tree_out_info.
:- import_module parse_tree.parse_tree_out_term.
:- import_module parse_tree.prog_data.
:- import_module io.
:- import_module list.
:- import_module term.
:- import_module varset.
%---------------------------------------------------------------------------%
:- func mercury_type_list_to_string(tvarset, list(mer_type)) = string.
:- pred mercury_output_type(tvarset::in, var_name_print::in, mer_type::in,
io::di, io::uo) is det.
:- func mercury_type_to_string(tvarset, var_name_print, mer_type) = string.
:- pred mercury_format_type(tvarset::in, var_name_print::in, mer_type::in,
U::di, U::uo) is det <= output(U).
%---------------------------------------------------------------------------%
:- pred mercury_output_det(determinism::in, io::di, io::uo) is det.
:- func mercury_det_to_string(determinism) = string.
:- pred mercury_format_det(determinism::in, U::di, U::uo) is det <= output(U).
%---------------------------------------------------------------------------%
% Output an existential quantifier.
%
:- pred mercury_output_quantifier(tvarset::in, var_name_print::in,
existq_tvars::in, io::di, io::uo) is det.
:- func mercury_quantifier_to_string(tvarset, var_name_print, existq_tvars)
= string.
:- pred mercury_format_quantifier(tvarset::in, var_name_print::in,
existq_tvars::in, U::di, U::uo) is det <= output(U).
%---------------------------------------------------------------------------%
% Similar to mercury_output_vars/3, but prefixes each variable
% with `!' to indicate that it is a state variable.
%
:- pred mercury_output_state_vars(varset(T)::in, var_name_print::in,
list(var(T))::in, io::di, io::uo) is det.
%---------------------------------------------------------------------------%
% Output a cons_id, parenthesizing it if necessary.
%
:- pred mercury_output_cons_id(needs_brackets::in, cons_id::in,
io::di, io::uo) is det.
:- func mercury_cons_id_to_string(needs_brackets, cons_id) = string.
:- pred mercury_format_cons_id(needs_brackets::in, cons_id::in, U::di, U::uo)
is det <= output(U).
%---------------------------------------------------------------------------%
%
% Output sym_names, maybe with their arities.
%
% Use mercury_output_bracketed_sym_name when the sym_name has no arguments,
% otherwise use mercury_output_sym_name.
%
:- type needs_brackets
---> needs_brackets
% Needs brackets, if it is an op.
; does_not_need_brackets.
% Doesn't need brackets.
:- pred mercury_output_sym_name(sym_name::in, io::di, io::uo) is det.
:- pred mercury_format_sym_name(sym_name::in, U::di, U::uo)
is det <= output(U).
:- pred mercury_format_sym_name_ngt(needs_quotes::in, sym_name::in,
U::di, U::uo) is det <= output(U).
:- pred mercury_format_sym_name_and_arity(sym_name_and_arity::in, U::di, U::uo)
is det <= output(U).
%---------------------%
:- pred mercury_output_bracketed_sym_name(sym_name::in,
io::di, io::uo) is det.
:- func mercury_bracketed_sym_name_to_string(sym_name) = string.
:- pred mercury_format_bracketed_sym_name(sym_name::in,
U::di, U::uo) is det <= output(U).
%---------------------%
:- pred mercury_output_bracketed_sym_name_ngt(needs_quotes::in, sym_name::in,
io::di, io::uo) is det.
:- func mercury_bracketed_sym_name_to_string_ngt(needs_quotes, sym_name)
= string.
:- pred mercury_format_bracketed_sym_name_ngt(needs_quotes::in, sym_name::in,
U::di, U::uo) is det <= output(U).
%---------------------------------------------------------------------------%
:- pred mercury_output_constraint(tvarset::in, var_name_print::in,
prog_constraint::in, io::di, io::uo) is det.
:- func mercury_constraint_to_string(tvarset, prog_constraint) = string.
:- pred mercury_format_constraint(tvarset::in, var_name_print::in,
prog_constraint::in, U::di, U::uo) is det <= output(U).
:- pred mercury_format_prog_constraint_list(tvarset::in, var_name_print::in,
string::in, list(prog_constraint)::in, U::di, U::uo) is det
<= output(U).
:- pred mercury_output_class_context(tvarset::in, var_name_print::in,
prog_constraints::in, existq_tvars::in, io::di, io::uo) is det.
:- pred mercury_format_class_context(tvarset::in, var_name_print::in,
prog_constraints::in, existq_tvars::in, U::di, U::uo) is det <= output(U).
%---------------------------------------------------------------------------%
:- pred mercury_output_foreign_language_string(foreign_language::in,
io::di, io::uo) is det.
:- func mercury_foreign_language_to_string(foreign_language) = string.
:- pred mercury_format_foreign_language_string(foreign_language::in,
U::di, U::uo) is det <= output(U).
%---------------------------------------------------------------------------%
:- pred mercury_output_newline(int::in, io::di, io::uo) is det.
:- pred mercury_format_tabs(int::in,
U::di, U::uo) is det <= output(U).
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module parse_tree.parse_tree_to_term.
:- import_module int.
:- import_module string.
:- import_module term_io.
%---------------------------------------------------------------------------%
mercury_type_list_to_string(_, []) = "".
mercury_type_list_to_string(VarSet, [Type | Types]) = String :-
String0 = mercury_type_to_string(VarSet, print_name_only, Type),
String1 = mercury_comma_type_list_to_string(VarSet, Types),
string.append(String0, String1, String).
:- func mercury_comma_type_list_to_string(tvarset, list(mer_type)) = string.
mercury_comma_type_list_to_string(_, []) = "".
mercury_comma_type_list_to_string(VarSet, [Type | Types]) = String :-
String0 = mercury_type_to_string(VarSet, print_name_only, Type),
String1 = mercury_comma_type_list_to_string(VarSet, Types),
string.append_list([", ", String0, String1], String).
mercury_output_type(VarSet, VarNamePrint, Type, !IO) :-
mercury_format_type(VarSet, VarNamePrint, Type, !IO).
mercury_type_to_string(VarSet, VarNamePrint, Type) = String :-
mercury_format_type(VarSet, VarNamePrint, Type, "", String).
mercury_format_type(TypeVarSet, VarNamePrint, Type, !U) :-
% We convert to a term and then use mercury_format_term. The reason
% for this is that we have to be very careful about handling operators
% and precedence properly, and it is better to have the code to manage
% that in one place, rather than duplicated here.
%
unparse_type(Type, Term),
VarSet = varset.coerce(TypeVarSet),
mercury_format_term(VarSet, VarNamePrint, Term, !U).
%---------------------------------------------------------------------------%
mercury_output_det(Detism, !UI) :-
mercury_format_det(Detism, !UI).
mercury_det_to_string(detism_det) = "det".
mercury_det_to_string(detism_semi) = "semidet".
mercury_det_to_string(detism_non) = "nondet".
mercury_det_to_string(detism_multi) = "multi".
mercury_det_to_string(detism_cc_multi) = "cc_multi".
mercury_det_to_string(detism_cc_non) = "cc_nondet".
mercury_det_to_string(detism_failure) = "failure".
mercury_det_to_string(detism_erroneous) = "erroneous".
mercury_format_det(Detism, !U) :-
add_string(mercury_det_to_string(Detism), !U).
%---------------------------------------------------------------------------%
mercury_output_quantifier(TypeVarSet, VarNamePrint, ExistQVars, !IO) :-
mercury_format_quantifier(TypeVarSet, VarNamePrint, ExistQVars, !IO).
mercury_quantifier_to_string(TypeVarSet, VarNamePrint, ExistQVars) = String :-
mercury_format_quantifier(TypeVarSet, VarNamePrint, ExistQVars,
"", String).
mercury_format_quantifier(TypeVarSet, VarNamePrint, ExistQVars, !U) :-
(
ExistQVars = []
;
ExistQVars = [_ | _],
add_string("some [", !U),
mercury_format_vars(TypeVarSet, VarNamePrint, ExistQVars, !U),
add_string("] ", !U)
).
%---------------------------------------------------------------------------%
mercury_output_state_vars(VarSet, VarNamePrint, StateVars, !IO) :-
io.write_list(StateVars, ", ",
mercury_output_state_var(VarSet, VarNamePrint), !IO).
:- pred mercury_output_state_var(varset(T)::in, var_name_print::in, var(T)::in,
io::di, io::uo) is det.
mercury_output_state_var(VarSet, VarNamePrint, Var, !IO) :-
io.write_string("!", !IO),
mercury_output_var(VarSet, VarNamePrint, Var, !IO).
%---------------------------------------------------------------------------%
mercury_output_cons_id(NeedsBrackets, ConsId, !IO) :-
mercury_format_cons_id(NeedsBrackets, ConsId, !IO).
mercury_cons_id_to_string(NeedsBrackets, ConsId) = String :-
mercury_format_cons_id(NeedsBrackets, ConsId, "", String).
mercury_format_cons_id(NeedsBrackets, ConsId, !U) :-
(
ConsId = cons(Name, _, _),
(
NeedsBrackets = needs_brackets,
mercury_format_bracketed_sym_name(Name, !U)
;
NeedsBrackets = does_not_need_brackets,
mercury_format_sym_name(Name, !U)
)
;
ConsId = tuple_cons(_),
add_string("{}", !U)
;
ConsId = int_const(Int),
add_int(Int, !U)
;
ConsId = uint_const(UInt),
add_uint(UInt, !U)
;
ConsId = int8_const(Int8),
add_int8(Int8, !U)
;
ConsId = uint8_const(UInt8),
add_uint8(UInt8, !U)
;
ConsId = int16_const(Int16),
add_int16(Int16, !U)
;
ConsId = uint16_const(UInt16),
add_uint16(UInt16, !U)
;
ConsId = int32_const(Int32),
add_int32(Int32, !U)
;
ConsId = uint32_const(UInt32),
add_uint32(UInt32, !U)
;
ConsId = int64_const(Int64),
add_int(Int64, !U) % XXX INT64
;
ConsId = uint64_const(UInt64),
add_int(UInt64, !U) % XXX INT64
;
ConsId = float_const(Float),
add_float(Float, !U)
;
ConsId = char_const(Char),
add_string(term_io.quoted_char(Char), !U)
;
ConsId = string_const(Str),
add_quoted_string(Str, !U)
;
ConsId = impl_defined_const(Name),
add_string("$", !U),
add_string(Name, !U)
;
ConsId = closure_cons(ShroudedPredProcId, _EvalMethod),
% XXX Should probably print this out in name/arity form.
ShroudedPredProcId = shrouded_pred_proc_id(PredInt, ProcInt),
add_string("<closure_cons(", !U),
add_int(PredInt, !U),
add_string(", ", !U),
add_int(ProcInt, !U),
% add_string(", ", !U),
% add_lambda_eval_method(EvalMethod, !U),
add_string(")>", !U)
;
ConsId = type_ctor_info_const(ModuleName, Type, Arity),
ModuleString = sym_name_to_string(ModuleName),
string.int_to_string(Arity, ArityString),
add_strings(["<type_ctor_info for ",
ModuleString, ".", Type, "/", ArityString, ">"], !U)
;
ConsId = base_typeclass_info_const(ModuleName, ClassId, InstanceNum,
InstanceString),
ModuleString = sym_name_to_string(ModuleName),
ClassId = class_id(ClassName, ClassArity),
add_string("<base_typeclass_info for ", !U),
add_string("class_id(", !U),
mercury_format_sym_name(ClassName, !U),
add_string(", ", !U),
add_int(ClassArity, !U),
add_string(")", !U),
( if ModuleString \= "some bogus module name" then
add_strings([" from module ", ModuleString], !U)
else
true
),
add_format(", instance number %d (%s)>",
[i(InstanceNum), s(InstanceString)], !U)
;
ConsId = type_info_cell_constructor(_),
add_string("<type_info_cell_constructor>", !U)
;
ConsId = typeclass_info_cell_constructor,
add_string("<typeclass_info_cell_constructor>", !U)
;
ConsId = type_info_const(TIConstNum),
add_string("<type_info_cell_constructor " ++
int_to_string(TIConstNum) ++ ">", !U)
;
ConsId = typeclass_info_const(TCIConstNum),
add_string("<typeclass_info_cell_constructor " ++
int_to_string(TCIConstNum) ++ ">", !U)
;
ConsId = ground_term_const(ConstNum, SubConsId),
add_string("<ground_term_cell_constructor " ++
int_to_string(ConstNum) ++ ", ", !U),
mercury_format_cons_id(does_not_need_brackets, SubConsId, !U),
add_string(">", !U)
;
ConsId = tabling_info_const(_),
add_string("<tabling info>", !U)
;
ConsId = table_io_entry_desc(_),
add_string("<table_io_entry_desc>", !U)
;
ConsId = deep_profiling_proc_layout(_),
add_string("<deep_profiling_proc_layout>", !U)
).
%---------------------------------------------------------------------------%
mercury_output_sym_name(SymName, !IO) :-
mercury_format_sym_name_ngt(not_next_to_graphic_token, SymName, !IO).
mercury_format_sym_name(SymName, !U) :-
mercury_format_sym_name_ngt(not_next_to_graphic_token, SymName, !U).
mercury_format_sym_name_ngt(NextToGraphicToken, SymName, !U) :-
(
SymName = qualified(ModuleName, PredName),
mercury_format_bracketed_sym_name_ngt(next_to_graphic_token,
ModuleName, !U),
add_string(".", !U),
mercury_format_quoted_atom(next_to_graphic_token, PredName, !U)
;
SymName = unqualified(PredName),
mercury_format_quoted_atom(NextToGraphicToken, PredName, !U)
).
mercury_format_sym_name_and_arity(sym_name_arity(SymName, Arity), !U) :-
mercury_format_sym_name(SymName, !U),
add_char('/', !U),
add_int(Arity, !U).
%---------------------%
mercury_output_bracketed_sym_name(SymName, !IO) :-
mercury_output_bracketed_sym_name_ngt(not_next_to_graphic_token, SymName,
!IO).
mercury_bracketed_sym_name_to_string(SymName) =
mercury_bracketed_sym_name_to_string_ngt(not_next_to_graphic_token,
SymName).
mercury_format_bracketed_sym_name(SymName, !U) :-
mercury_format_bracketed_sym_name_ngt(not_next_to_graphic_token, SymName,
!U).
%---------------------%
mercury_output_bracketed_sym_name_ngt(NextToGraphicToken, SymName, !IO) :-
mercury_format_bracketed_sym_name_ngt(NextToGraphicToken, SymName, !IO).
mercury_bracketed_sym_name_to_string_ngt(NextToGraphicToken, SymName) = Str :-
mercury_format_bracketed_sym_name_ngt(NextToGraphicToken, SymName,
"", Str).
mercury_format_bracketed_sym_name_ngt(NextToGraphicToken, SymName, !U) :-
(
SymName = qualified(ModuleName, Name),
add_string("(", !U),
mercury_format_bracketed_sym_name_ngt(next_to_graphic_token,
ModuleName, !U),
add_string(".", !U),
mercury_format_bracketed_atom(next_to_graphic_token, Name, !U),
add_string(")", !U)
;
SymName = unqualified(Name),
mercury_format_bracketed_atom(NextToGraphicToken, Name, !U)
).
%---------------------------------------------------------------------------%
mercury_output_constraint(TypeVarSet, VarNamePrint, Constraint, !IO) :-
mercury_format_constraint(TypeVarSet, VarNamePrint, Constraint, !IO).
mercury_constraint_to_string(TypeVarSet, Constraint) = String :-
mercury_format_constraint(TypeVarSet, print_name_only, Constraint,
"", String).
mercury_format_constraint(TypeVarSet, VarNamePrint, Constraint, !U) :-
Constraint = constraint(Name, Types),
mercury_format_sym_name(Name, !U),
add_string("(", !U),
add_list(Types, ", ", mercury_format_type(TypeVarSet, VarNamePrint), !U),
add_string(")", !U).
mercury_format_prog_constraint_list(TypeVarSet, VarNamePrint, Operator,
Constraints, !U) :-
(
Constraints = []
;
Constraints = [_ | _],
add_strings([" ", Operator, " ("], !U),
add_list(Constraints, ", ",
mercury_format_constraint(TypeVarSet, VarNamePrint), !U),
add_string(")", !U)
).
%---------------------------------------------------------------------------%
mercury_output_class_context(TypeVarSet, VarNamePrint,
ClassContext, ExistQVars, !IO) :-
mercury_format_class_context(TypeVarSet, VarNamePrint,
ClassContext, ExistQVars, !IO).
mercury_format_class_context(TypeVarSet, VarNamePrint,
ClassContext, ExistQVars, !U) :-
ClassContext = constraints(UnivCs, ExistCs),
mercury_format_prog_constraint_list(TypeVarSet, VarNamePrint, "=>",
ExistCs, !U),
( if
ExistQVars = [],
ExistCs = []
then
true
else
% XXX What prints the matching open parenthesis?
% And does it print the open in *exactly* the same set of situations
% in which we print the close?
add_string(")", !U)
),
mercury_format_prog_constraint_list(TypeVarSet, VarNamePrint, "<=",
UnivCs, !U).
%---------------------------------------------------------------------------%
mercury_output_foreign_language_string(Lang, !IO) :-
mercury_format_foreign_language_string(Lang, !IO).
mercury_foreign_language_to_string(Lang) = String :-
mercury_format_foreign_language_string(Lang, "", String).
mercury_format_foreign_language_string(Lang, !U) :-
add_string("""" ++ foreign_language_string(Lang) ++ """", !U).
%---------------------------------------------------------------------------%
mercury_output_newline(Indent, !IO) :-
io.write_char('\n', !IO),
mercury_format_tabs(Indent, !IO).
mercury_format_tabs(Indent, !U) :-
( if Indent > 0 then
add_string("\t", !U),
mercury_format_tabs(Indent - 1, !U)
else
true
).
%---------------------------------------------------------------------------%
:- end_module parse_tree.mercury_to_mercury.
%---------------------------------------------------------------------------%