Files
mercury/library/string.format.m
Zoltan Somogyi 9c248726a6 Add uint{64,}_to_lc_hex_string.
library/string.m:
    We long had uint_to_hex_string and uint_to_uc_hex_string. Add
    uint_to_lc_hex_string as well, and make uint_to_hex_string call it.
    This way, users don't have to remember which of the upper and lower
    case versions is defined, and which is missing.

    Do the same for the 64 bit version.

NEWS:
    Announce the new functions.

library/string.format.m:
    Call the new functions.
2021-01-22 17:18:57 +11:00

2162 lines
77 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ts=4 sw=4 et ft=mercury
%---------------------------------------------------------------------------%
% Copyright (C) 2014-2015, 2017-2021 The Mercury team.
% This file is distributed under the terms specified in COPYING.LIB.
%---------------------------------------------------------------------------%
%
% File: string.format.m.
%
% This module implements string.format.
%
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- module string.format.
:- interface.
:- import_module list.
:- import_module string.parse_util.
%---------------------------------------------------------------------------%
% The implementation of string.format. This is exported for use
% by string.m.
%
:- pred format_impl(string::in, list(poly_type)::in, string::out) is det.
% Functions to print characters, strings, signed and unsigned ints,
% and floats. These versions of these functions are not called
% by the runtime, but they have calls generated to them by
% compiler/format_call.m. By having specialized versions for each
% possible combination of which of parameters (width and precision)
% are specified, we can avoid requiring the compiler to generate code
% that allocates memory on the heap.
%
% NOTE: if you add new predicates here then you must also update the
% predicate simplify_may_introduce_calls/3 in compiler/simplify_proc.m.
%
:- pred format_char_component_nowidth(string_format_flags::in,
char::in, string::out) is det.
:- pred format_char_component_width(string_format_flags::in,
int::in, char::in, string::out) is det.
:- pred format_string_component_nowidth_noprec(string_format_flags::in,
string::in, string::out) is det.
:- pred format_string_component_nowidth_prec(string_format_flags::in,
int::in, string::in, string::out) is det.
:- pred format_string_component_width_noprec(string_format_flags::in,
int::in, string::in, string::out) is det.
:- pred format_string_component_width_prec(string_format_flags::in,
int::in, int::in, string::in, string::out) is det.
:- pred format_signed_int_component_nowidth_noprec(string_format_flags::in,
int::in, string::out) is det.
:- pred format_signed_int_component_nowidth_prec(string_format_flags::in,
int::in, int::in, string::out) is det.
:- pred format_signed_int_component_width_noprec(string_format_flags::in,
int::in, int::in, string::out) is det.
:- pred format_signed_int_component_width_prec(string_format_flags::in,
int::in, int::in, int::in, string::out) is det.
:- pred format_unsigned_int_component_nowidth_noprec(string_format_flags::in,
string_format_int_base::in, int::in, string::out) is det.
:- pred format_unsigned_int_component_nowidth_prec(string_format_flags::in,
int::in, string_format_int_base::in, int::in, string::out) is det.
:- pred format_unsigned_int_component_width_noprec(string_format_flags::in,
int::in, string_format_int_base::in, int::in, string::out) is det.
:- pred format_unsigned_int_component_width_prec(string_format_flags::in,
int::in, int::in, string_format_int_base::in, int::in, string::out) is det.
:- pred format_uint_component_nowidth_noprec(string_format_flags::in,
string_format_int_base::in, uint::in, string::out) is det.
:- pred format_uint_component_nowidth_prec(string_format_flags::in,
int::in, string_format_int_base::in, uint::in, string::out) is det.
:- pred format_uint_component_width_noprec(string_format_flags::in,
int::in, string_format_int_base::in, uint::in, string::out) is det.
:- pred format_uint_component_width_prec(string_format_flags::in,
int::in, int::in, string_format_int_base::in, uint::in, string::out)
is det.
:- pred format_signed_int64_component_nowidth_noprec(string_format_flags::in,
int64::in, string::out) is det.
:- pred format_signed_int64_component_nowidth_prec(string_format_flags::in,
int::in, int64::in, string::out) is det.
:- pred format_signed_int64_component_width_noprec(string_format_flags::in,
int::in, int64::in, string::out) is det.
:- pred format_signed_int64_component_width_prec(string_format_flags::in,
int::in, int::in, int64::in, string::out) is det.
:- pred format_unsigned_int64_component_nowidth_noprec(string_format_flags::in,
string_format_int_base::in, int64::in, string::out) is det.
:- pred format_unsigned_int64_component_nowidth_prec(string_format_flags::in,
int::in, string_format_int_base::in, int64::in, string::out) is det.
:- pred format_unsigned_int64_component_width_noprec(string_format_flags::in,
int::in, string_format_int_base::in, int64::in, string::out) is det.
:- pred format_unsigned_int64_component_width_prec(string_format_flags::in,
int::in, int::in, string_format_int_base::in, int64::in, string::out)
is det.
:- pred format_uint64_component_nowidth_noprec(string_format_flags::in,
string_format_int_base::in, uint64::in, string::out) is det.
:- pred format_uint64_component_nowidth_prec(string_format_flags::in,
int::in, string_format_int_base::in, uint64::in, string::out) is det.
:- pred format_uint64_component_width_noprec(string_format_flags::in,
int::in, string_format_int_base::in, uint64::in, string::out) is det.
:- pred format_uint64_component_width_prec(string_format_flags::in,
int::in, int::in, string_format_int_base::in, uint64::in, string::out)
is det.
:- pred format_float_component_nowidth_noprec(string_format_flags::in,
string_format_float_kind::in, float::in, string::out) is det.
:- pred format_float_component_nowidth_prec(string_format_flags::in,
int::in, string_format_float_kind::in, float::in, string::out) is det.
:- pred format_float_component_width_noprec(string_format_flags::in,
int::in, string_format_float_kind::in, float::in, string::out) is det.
:- pred format_float_component_width_prec(string_format_flags::in,
int::in, int::in, string_format_float_kind::in, float::in, string::out)
is det.
:- pred format_cast_int8_to_int(int8::in, int::out) is det.
:- pred format_cast_int16_to_int(int16::in, int::out) is det.
:- pred format_cast_int32_to_int(int32::in, int::out) is det.
:- pred format_cast_uint8_to_uint(uint8::in, uint::out) is det.
:- pred format_cast_uint16_to_uint(uint16::in, uint::out) is det.
:- pred format_cast_uint32_to_uint(uint32::in, uint::out) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module bool.
:- import_module exception.
:- import_module float.
:- import_module int.
:- import_module integer.
:- import_module require.
:- import_module string.parse_runtime.
:- import_module uint.
:- use_module int8.
:- use_module int16.
:- use_module int32.
:- use_module int64.
:- use_module uint8.
:- use_module uint16.
:- use_module uint32.
:- use_module uint64.
%---------------------------------------------------------------------------%
:- pragma foreign_decl("C",
"
#include ""mercury_float.h"" // for MR_float_to_string
// The following macro should expand to MR_TRUE if the C grades should
// implement string.format using C's sprintf function.
// Setting it to MR_FALSE will cause string.format to use the Mercury
// implementation of string formatting in C grades.
#define ML_USE_SPRINTF MR_TRUE
").
%---------------------------------------------------------------------------%
string.format.format_impl(FormatString, PolyList, String) :-
% The call tree of this predicate should be optimised to turn over
% the least amount of memory possible, since memory usage is a significant
% problem for programs which do a lot of formatted IO.
%
% XXX The repeated string appends performed in the call tree
% do still allocate nontrivial amounts of memory for temporaries.
%
% XXX ILSEQ to_char_list cannot handle ill-formed sequences in the format
% string. Ideally they would be treated like constant strings.
Chars = to_char_list(FormatString),
parse_format_string(Chars, PolyList, 1, Specs, Errors),
(
Errors = [],
specs_to_strings(Specs, SpecStrs),
String = string.append_list(SpecStrs)
;
Errors = [HeadError | _],
% In the common cases of missing or extra PolyTypes, all the errors
% after the first may be avalanche errors, and would probably be more
% confusing than helpful. This is why we traditionally print a message
% only for the first one.
%
% XXX We should try printing messages for all the errors, not just
% the first, and see whether the usefulness of the extra information
% outweighs the costs of any extra confusion.
Msg = string_format_error_to_msg(HeadError),
error("string.format", Msg)
).
%---------------------------------------------------------------------------%
:- pred specs_to_strings(list(string_format_spec)::in,
list(string)::out) is det.
specs_to_strings([], []).
specs_to_strings([Spec | Specs], [String | Strings]) :-
spec_to_string(Spec, String),
specs_to_strings(Specs, Strings).
:- pred spec_to_string(string_format_spec::in, string::out) is det.
spec_to_string(Spec, String) :-
(
% Constant strings.
Spec = spec_constant_string(String)
;
% Signed int conversion specifiers.
Spec = spec_signed_int(Flags, MaybeWidth, MaybePrec, SizedInt),
( if SizedInt = sized_int64(Int64) then
format_signed_int64_component(Flags, MaybeWidth, MaybePrec, Int64,
String)
else
Int = sized_int_to_int(SizedInt),
format_signed_int_component(Flags, MaybeWidth, MaybePrec, Int,
String)
)
;
% Unsigned int conversion specifiers (for signed values).
Spec = spec_unsigned_int(Flags, MaybeWidth, MaybePrec, Base, SizedInt),
( if SizedInt = sized_int64(Int64) then
format_unsigned_int64_component(Flags, MaybeWidth, MaybePrec, Base,
Int64, String)
else
Int = sized_int_to_int(SizedInt),
format_unsigned_int_component(Flags, MaybeWidth, MaybePrec, Base,
Int, String)
)
;
% Unsigned int conversion specifiers (for unsigned values).
Spec = spec_uint(Flags, MaybeWidth, MaybePrec, Base, SizedUInt),
( if SizedUInt = sized_uint64(UInt64) then
format_uint64_component(Flags, MaybeWidth, MaybePrec, Base, UInt64,
String)
else
UInt = sized_uint_to_uint(SizedUInt),
format_uint_component(Flags, MaybeWidth, MaybePrec, Base, UInt,
String)
)
;
% Float conversion specifiers.
Spec = spec_float(Flags, MaybeWidth, MaybePrec, Kind, Float),
format_float_component(Flags, MaybeWidth, MaybePrec, Kind, Float,
String)
;
% Char conversion specifiers.
Spec = spec_char(Flags, MaybeWidth, Char),
format_char_component(Flags, MaybeWidth, Char, String)
;
% String conversion specifiers.
Spec = spec_string(Flags, MaybeWidth, MaybePrec, Str),
format_string_component(Flags, MaybeWidth, MaybePrec, Str, String)
).
%---------------------------------------------------------------------------%
format_char_component_nowidth(Flags, Char, String) :-
MaybeWidth = no_specified_width,
format_char_component(Flags, MaybeWidth, Char, String).
format_char_component_width(Flags, Width, Char, String) :-
MaybeWidth = specified_width(Width),
format_char_component(Flags, MaybeWidth, Char, String).
%---------------------------------------------------------------------------%
format_string_component_nowidth_noprec(Flags, Str, String) :-
MaybeWidth = no_specified_width,
MaybePrec = no_specified_prec,
format_string_component(Flags, MaybeWidth, MaybePrec, Str, String).
format_string_component_nowidth_prec(Flags, Prec, Str, String) :-
MaybeWidth = no_specified_width,
MaybePrec = specified_prec(Prec),
format_string_component(Flags, MaybeWidth, MaybePrec, Str, String).
format_string_component_width_noprec(Flags, Width, Str, String) :-
MaybeWidth = specified_width(Width),
MaybePrec = no_specified_prec,
format_string_component(Flags, MaybeWidth, MaybePrec, Str, String).
format_string_component_width_prec(Flags, Width, Prec, Str, String) :-
MaybeWidth = specified_width(Width),
MaybePrec = specified_prec(Prec),
format_string_component(Flags, MaybeWidth, MaybePrec, Str, String).
%---------------------------------------------------------------------------%
format_signed_int_component_nowidth_noprec(Flags, Int, String) :-
MaybeWidth = no_specified_width,
MaybePrec = no_specified_prec,
format_signed_int_component(Flags, MaybeWidth, MaybePrec, Int, String).
format_signed_int_component_nowidth_prec(Flags, Prec, Int, String) :-
MaybeWidth = no_specified_width,
MaybePrec = specified_prec(Prec),
format_signed_int_component(Flags, MaybeWidth, MaybePrec, Int, String).
format_signed_int_component_width_noprec(Flags, Width, Int, String) :-
MaybeWidth = specified_width(Width),
MaybePrec = no_specified_prec,
format_signed_int_component(Flags, MaybeWidth, MaybePrec, Int, String).
format_signed_int_component_width_prec(Flags, Width, Prec, Int, String) :-
MaybeWidth = specified_width(Width),
MaybePrec = specified_prec(Prec),
format_signed_int_component(Flags, MaybeWidth, MaybePrec, Int, String).
%---------------------------------------------------------------------------%
format_unsigned_int_component_nowidth_noprec(Flags, Base, Int, String) :-
MaybeWidth = no_specified_width,
MaybePrec = no_specified_prec,
format_unsigned_int_component(Flags, MaybeWidth, MaybePrec, Base, Int,
String).
format_unsigned_int_component_nowidth_prec(Flags, Prec, Base, Int, String) :-
MaybeWidth = no_specified_width,
MaybePrec = specified_prec(Prec),
format_unsigned_int_component(Flags, MaybeWidth, MaybePrec, Base, Int,
String).
format_unsigned_int_component_width_noprec(Flags, Width, Base, Int, String) :-
MaybeWidth = specified_width(Width),
MaybePrec = no_specified_prec,
format_unsigned_int_component(Flags, MaybeWidth, MaybePrec, Base, Int,
String).
format_unsigned_int_component_width_prec(Flags, Width, Prec, Base, Int,
String) :-
MaybeWidth = specified_width(Width),
MaybePrec = specified_prec(Prec),
format_unsigned_int_component(Flags, MaybeWidth, MaybePrec, Base, Int,
String).
%---------------------------------------------------------------------------%
format_uint_component_nowidth_noprec(Flags, Base, UInt, String) :-
MaybeWidth = no_specified_width,
MaybePrec = no_specified_prec,
format_uint_component(Flags, MaybeWidth, MaybePrec, Base, UInt,
String).
format_uint_component_nowidth_prec(Flags, Prec, Base, UInt, String) :-
MaybeWidth = no_specified_width,
MaybePrec = specified_prec(Prec),
format_uint_component(Flags, MaybeWidth, MaybePrec, Base, UInt,
String).
format_uint_component_width_noprec(Flags, Width, Base, UInt, String) :-
MaybeWidth = specified_width(Width),
MaybePrec = no_specified_prec,
format_uint_component(Flags, MaybeWidth, MaybePrec, Base, UInt,
String).
format_uint_component_width_prec(Flags, Width, Prec, Base, UInt,
String) :-
MaybeWidth = specified_width(Width),
MaybePrec = specified_prec(Prec),
format_uint_component(Flags, MaybeWidth, MaybePrec, Base, UInt,
String).
%---------------------------------------------------------------------------%
format_signed_int64_component_nowidth_noprec(Flags, Int64, String) :-
MaybeWidth = no_specified_width,
MaybePrec = no_specified_prec,
format_signed_int64_component(Flags, MaybeWidth, MaybePrec, Int64,
String).
format_signed_int64_component_nowidth_prec(Flags, Prec, Int64, String) :-
MaybeWidth = no_specified_width,
MaybePrec = specified_prec(Prec),
format_signed_int64_component(Flags, MaybeWidth, MaybePrec, Int64,
String).
format_signed_int64_component_width_noprec(Flags, Width, Int64, String) :-
MaybeWidth = specified_width(Width),
MaybePrec = no_specified_prec,
format_signed_int64_component(Flags, MaybeWidth, MaybePrec, Int64,
String).
format_signed_int64_component_width_prec(Flags, Width, Prec, Int64,
String) :-
MaybeWidth = specified_width(Width),
MaybePrec = specified_prec(Prec),
format_signed_int64_component(Flags, MaybeWidth, MaybePrec, Int64,
String).
%---------------------------------------------------------------------------%
format_unsigned_int64_component_nowidth_noprec(Flags, Base, Int64, String) :-
MaybeWidth = no_specified_width,
MaybePrec = no_specified_prec,
format_unsigned_int64_component(Flags, MaybeWidth, MaybePrec, Base, Int64,
String).
format_unsigned_int64_component_nowidth_prec(Flags, Prec, Base, Int64,
String) :-
MaybeWidth = no_specified_width,
MaybePrec = specified_prec(Prec),
format_unsigned_int64_component(Flags, MaybeWidth, MaybePrec, Base, Int64,
String).
format_unsigned_int64_component_width_noprec(Flags, Width, Base, Int64,
String) :-
MaybeWidth = specified_width(Width),
MaybePrec = no_specified_prec,
format_unsigned_int64_component(Flags, MaybeWidth, MaybePrec, Base, Int64,
String).
format_unsigned_int64_component_width_prec(Flags, Width, Prec, Base, Int64,
String) :-
MaybeWidth = specified_width(Width),
MaybePrec = specified_prec(Prec),
format_unsigned_int64_component(Flags, MaybeWidth, MaybePrec, Base, Int64,
String).
%---------------------------------------------------------------------------%
format_uint64_component_nowidth_noprec(Flags, Base, UInt64, String) :-
MaybeWidth = no_specified_width,
MaybePrec = no_specified_prec,
format_uint64_component(Flags, MaybeWidth, MaybePrec, Base, UInt64,
String).
format_uint64_component_nowidth_prec(Flags, Prec, Base, UInt64, String) :-
MaybeWidth = no_specified_width,
MaybePrec = specified_prec(Prec),
format_uint64_component(Flags, MaybeWidth, MaybePrec, Base, UInt64,
String).
format_uint64_component_width_noprec(Flags, Width, Base, UInt64, String) :-
MaybeWidth = specified_width(Width),
MaybePrec = no_specified_prec,
format_uint64_component(Flags, MaybeWidth, MaybePrec, Base, UInt64,
String).
format_uint64_component_width_prec(Flags, Width, Prec, Base, UInt64,
String) :-
MaybeWidth = specified_width(Width),
MaybePrec = specified_prec(Prec),
format_uint64_component(Flags, MaybeWidth, MaybePrec, Base, UInt64,
String).
%---------------------------------------------------------------------------%
format_float_component_nowidth_noprec(Flags, Kind, Float, String) :-
MaybeWidth = no_specified_width,
MaybePrec = no_specified_prec,
format_float_component(Flags, MaybeWidth, MaybePrec, Kind, Float, String).
format_float_component_nowidth_prec(Flags, Prec, Kind, Float, String) :-
MaybeWidth = no_specified_width,
MaybePrec = specified_prec(Prec),
format_float_component(Flags, MaybeWidth, MaybePrec, Kind, Float, String).
format_float_component_width_noprec(Flags, Width, Kind, Float, String) :-
MaybeWidth = specified_width(Width),
MaybePrec = no_specified_prec,
format_float_component(Flags, MaybeWidth, MaybePrec, Kind, Float, String).
format_float_component_width_prec(Flags, Width, Prec, Kind, Float, String) :-
MaybeWidth = specified_width(Width),
MaybePrec = specified_prec(Prec),
format_float_component(Flags, MaybeWidth, MaybePrec, Kind, Float, String).
%---------------------------------------------------------------------------%
:- pred format_char_component(string_format_flags::in,
string_format_maybe_width::in, char::in, string::out) is det.
format_char_component(Flags, MaybeWidth, Char, String) :-
( if using_sprintf_for_char(Char) then
FormatStr = make_format_sprintf(Flags, MaybeWidth, no_specified_prec,
"", "c"),
String = native_format_char(FormatStr, Char)
else
String = format_char(Flags, MaybeWidth, Char)
).
:- pred format_string_component(string_format_flags::in,
string_format_maybe_width::in, string_format_maybe_prec::in,
string::in, string::out) is det.
format_string_component(Flags, MaybeWidth, MaybePrec, Str, String) :-
( if
(
using_sprintf,
Flags = string_format_flags(flag_hash_clear, flag_space_clear,
flag_zero_clear, flag_minus_clear, flag_plus_clear),
MaybeWidth = no_specified_width,
MaybePrec = no_specified_prec
;
using_sprintf_for_string(Str)
)
then
FormatStr = make_format_sprintf(Flags, MaybeWidth, MaybePrec, "", "s"),
String = native_format_string(FormatStr, Str)
else
String = format_string(Flags, MaybeWidth, MaybePrec, Str)
).
:- pred format_signed_int_component(string_format_flags::in,
string_format_maybe_width::in, string_format_maybe_prec::in,
int::in, string::out) is det.
format_signed_int_component(Flags, MaybeWidth, MaybePrec, Int, String) :-
( if using_sprintf then
% XXX The "d" could be "i"; we don't keep track.
FormatStr = make_format_sprintf(Flags, MaybeWidth, MaybePrec,
int_length_modifier, "d"),
String = native_format_int(FormatStr, Int)
else
String = format_signed_int(Flags, MaybeWidth, MaybePrec, Int)
).
:- pred format_unsigned_int_component(string_format_flags::in,
string_format_maybe_width::in, string_format_maybe_prec::in,
string_format_int_base::in, int::in, string::out) is det.
format_unsigned_int_component(Flags, MaybeWidth, MaybePrec, Base, Int,
String) :-
( if using_sprintf then
( Base = base_octal, SpecChar = "o"
; Base = base_decimal, SpecChar = "u"
; Base = base_hex_lc, SpecChar = "x"
; Base = base_hex_uc, SpecChar = "X"
; Base = base_hex_p, SpecChar = "p"
),
FormatStr = make_format_sprintf(Flags, MaybeWidth, MaybePrec,
int_length_modifier, SpecChar),
String = native_format_int(FormatStr, Int)
else
UInt = cast_from_int(Int),
String = format_uint(Flags, MaybeWidth, MaybePrec, Base, UInt)
).
:- pred format_uint_component(string_format_flags::in,
string_format_maybe_width::in, string_format_maybe_prec::in,
string_format_int_base::in, uint::in, string::out) is det.
format_uint_component(Flags, MaybeWidth, MaybePrec, Base, UInt, String) :-
( if using_sprintf then
( Base = base_octal, SpecChar = "o"
; Base = base_decimal, SpecChar = "u"
; Base = base_hex_lc, SpecChar = "x"
; Base = base_hex_uc, SpecChar = "X"
; Base = base_hex_p, SpecChar = "p"
),
FormatStr = make_format_sprintf(Flags, MaybeWidth, MaybePrec,
int_length_modifier, SpecChar),
String = native_format_uint(FormatStr, UInt)
else
String = format_uint(Flags, MaybeWidth, MaybePrec, Base, UInt)
).
:- pred format_signed_int64_component(string_format_flags::in,
string_format_maybe_width::in, string_format_maybe_prec::in,
int64::in, string::out) is det.
format_signed_int64_component(Flags, MaybeWidth, MaybePrec, Int64, String) :-
( if using_sprintf then
FormatStr = make_format_sprintf(Flags, MaybeWidth, MaybePrec,
"", int64_decimal_specifier),
String = native_format_int64(FormatStr, Int64)
else
String = format_signed_int64(Flags, MaybeWidth, MaybePrec, Int64)
).
:- pred format_unsigned_int64_component(string_format_flags::in,
string_format_maybe_width::in, string_format_maybe_prec::in,
string_format_int_base::in, int64::in, string::out) is det.
format_unsigned_int64_component(Flags, MaybeWidth, MaybePrec, Base, Int64,
String) :-
( if using_sprintf then
( Base = base_octal, Spec = uint64_octal_specifier
; Base = base_decimal, Spec = uint64_decimal_specifier
; Base = base_hex_lc, Spec = uint64_hex_lc_specifier
; Base = base_hex_uc, Spec = uint64_hex_uc_specifier
; Base = base_hex_p, Spec = uint64_hex_p_specifier
),
FormatStr = make_format_sprintf(Flags, MaybeWidth, MaybePrec,
"", Spec),
String = native_format_int64(FormatStr, Int64)
else
UInt64 = uint64.cast_from_int64(Int64),
String = format_uint64(Flags, MaybeWidth, MaybePrec, Base, UInt64)
).
:- pred format_uint64_component(string_format_flags::in,
string_format_maybe_width::in, string_format_maybe_prec::in,
string_format_int_base::in, uint64::in, string::out) is det.
format_uint64_component(Flags, MaybeWidth, MaybePrec, Base, UInt64, String) :-
( if using_sprintf then
( Base = base_octal, Spec = uint64_octal_specifier
; Base = base_decimal, Spec = uint64_decimal_specifier
; Base = base_hex_lc, Spec = uint64_hex_lc_specifier
; Base = base_hex_uc, Spec = uint64_hex_uc_specifier
; Base = base_hex_p, Spec = uint64_hex_p_specifier
),
FormatStr = make_format_sprintf(Flags, MaybeWidth, MaybePrec,
"", Spec),
String = native_format_uint64(FormatStr, UInt64)
else
String = format_uint64(Flags, MaybeWidth, MaybePrec, Base, UInt64)
).
:- pred format_float_component(string_format_flags::in,
string_format_maybe_width::in, string_format_maybe_prec::in,
string_format_float_kind::in, float::in, string::out) is det.
format_float_component(Flags, MaybeWidth, MaybePrec, Kind, Float, String) :-
( if
is_finite(Float),
using_sprintf
then
( Kind = kind_e_scientific_lc, SpecChar = "e"
; Kind = kind_e_scientific_uc, SpecChar = "E"
; Kind = kind_f_plain_lc, SpecChar = "f"
; Kind = kind_f_plain_uc, SpecChar = "F"
; Kind = kind_g_flexible_lc, SpecChar = "g"
; Kind = kind_g_flexible_uc, SpecChar = "G"
),
FormatStr = make_format_sprintf(Flags, MaybeWidth, MaybePrec,
"", SpecChar),
String = native_format_float(FormatStr, Float)
else
String = format_float(Flags, MaybeWidth, MaybePrec, Kind, Float)
).
%---------------------------------------------------------------------------%
% Are we using C's sprintf? All backends other than C return false.
% Note that any backends which return true for using_sprintf/0 must
% also implement:
%
% int_length_modifier/0
% native_format_float/2
% native_format_int/2
% native_format_string/2
% native_format_char/2
% native_format_uint/2
% native_format_int64/2
% native_format_uint64/2
%
:- pred using_sprintf is semidet.
:- pragma foreign_proc("C", using_sprintf,
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness, no_sharing],
"
SUCCESS_INDICATOR = ML_USE_SPRINTF;
").
:- pragma foreign_proc("C#", using_sprintf,
[will_not_call_mercury, promise_pure, thread_safe],
"
SUCCESS_INDICATOR = false;
").
:- pragma foreign_proc("Java", using_sprintf,
[will_not_call_mercury, promise_pure, thread_safe],
"
SUCCESS_INDICATOR = false;
").
:- pred using_sprintf_for_char(char::in) is semidet.
using_sprintf_for_char(_) :-
semidet_fail.
:- pragma foreign_proc("C",
using_sprintf_for_char(Char::in),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness, no_sharing],
"
// sprintf %c specifier is inadequate for multi-byte UTF-8 characters.
SUCCESS_INDICATOR = ML_USE_SPRINTF && MR_is_ascii(Char);
").
:- pred using_sprintf_for_string(string::in) is semidet.
using_sprintf_for_string(_) :-
semidet_fail.
:- pragma foreign_proc("C",
using_sprintf_for_string(Str::in),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness, no_sharing],
"
const char *s;
SUCCESS_INDICATOR = ML_USE_SPRINTF;
for (s = Str; *s != '\\0'; s++) {
// sprintf %s specifier is inadequate for multi-byte UTF-8 characters,
// if there is a field width or precision specified.
if (!MR_utf8_is_single_byte(*s)) {
SUCCESS_INDICATOR = MR_FALSE;
break;
}
}
").
% Construct a format string suitable to passing to sprintf.
%
:- func make_format_sprintf(string_format_flags, string_format_maybe_width,
string_format_maybe_prec, string, string) = string.
make_format_sprintf(Flags, MaybeWidth, MaybePrec, LengthMod, Spec) = String :-
Flags = string_format_flags(FlagHash, FlagSpace, FlagZero,
FlagMinus, FlagPlus),
( FlagHash = flag_hash_clear, FlagHashStr = ""
; FlagHash = flag_hash_set, FlagHashStr = "#"
),
( FlagSpace = flag_space_clear, FlagSpaceStr = ""
; FlagSpace = flag_space_set, FlagSpaceStr = " "
),
( FlagZero = flag_zero_clear, FlagZeroStr = ""
; FlagZero = flag_zero_set, FlagZeroStr = "0"
),
( FlagMinus = flag_minus_clear, FlagMinusStr = ""
; FlagMinus = flag_minus_set, FlagMinusStr = "-"
),
( FlagPlus = flag_plus_clear, FlagPlusStr = ""
; FlagPlus = flag_plus_set, FlagPlusStr = "+"
),
(
MaybeWidth = specified_width(Width),
WidthStr = int_to_string(Width)
;
MaybeWidth = no_specified_width,
WidthStr = ""
),
(
MaybePrec = specified_prec(Prec),
PrecPrefixStr = ".",
PrecStr = int_to_string(Prec)
;
MaybePrec = no_specified_prec,
PrecPrefixStr = "",
PrecStr = ""
),
String = string.append_list(["%",
FlagHashStr, FlagSpaceStr, FlagZeroStr, FlagMinusStr, FlagPlusStr,
WidthStr, PrecPrefixStr, PrecStr, LengthMod, Spec]).
:- func int_length_modifier = string.
:- pragma no_determinism_warning(int_length_modifier/0).
:- pragma foreign_proc("C",
int_length_modifier = (LengthModifier::out),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness, no_sharing],
"{
MR_make_aligned_string(LengthModifier, MR_INTEGER_LENGTH_MODIFIER);
}").
int_length_modifier = _ :-
% This predicate is only called if using_sprintf/0, so we produce an error
% by default.
error("string.int_length_modifier/0 not defined").
% NOTE: C does not provide a way to determine the length modifier for the
% intN_t and uintN_t types in isolation. For int64s, the [oxXup] specifiers
% are all handled by casting to a uint64.
:- func int64_decimal_specifier = string.
:- pragma no_determinism_warning(int64_decimal_specifier/0).
:- pragma foreign_proc("C",
int64_decimal_specifier = (Spec::out),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness, no_sharing],
"
MR_make_aligned_string(Spec, PRId64);
").
int64_decimal_specifier = _ :-
% This predicate is only called if using_sprintf/0, so we produce an error
% by default.
error("string.int64_decimal_specifier/0 not defined").
:- func uint64_octal_specifier = string.
:- pragma no_determinism_warning(uint64_octal_specifier/0).
:- pragma foreign_proc("C",
uint64_octal_specifier = (Spec::out),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness, no_sharing],
"
MR_make_aligned_string(Spec, PRIo64);
").
uint64_octal_specifier = _ :-
% This predicate is only called if using_sprintf/0, so we produce an error
% by default.
error("string.uint64_octal_specifier/0 not defined").
:- func uint64_decimal_specifier = string.
:- pragma no_determinism_warning(uint64_decimal_specifier/0).
:- pragma foreign_proc("C",
uint64_decimal_specifier = (Spec::out),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness, no_sharing],
"
MR_make_aligned_string(Spec, PRIu64);
").
uint64_decimal_specifier = _ :-
% This predicate is only called if using_sprintf/0, so we produce an error
% by default.
error("string.uint64_decimal_specifier/0 not defined").
:- func uint64_hex_lc_specifier = string.
:- pragma no_determinism_warning(uint64_hex_lc_specifier/0).
:- pragma foreign_proc("C",
uint64_hex_lc_specifier = (Spec::out),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness, no_sharing],
"
MR_make_aligned_string(Spec, PRIx64);
").
uint64_hex_lc_specifier = _ :-
% This predicate is only called if using_sprintf/0, so we produce an error
% by default.
error("string.uint64_hex_lc_specifier/0 not defined").
:- func uint64_hex_uc_specifier = string.
:- pragma no_determinism_warning(uint64_hex_uc_specifier/0).
:- pragma foreign_proc("C",
uint64_hex_uc_specifier = (Spec::out),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness, no_sharing],
"
MR_make_aligned_string(Spec, PRIX64);
").
uint64_hex_uc_specifier = _ :-
% This predicate is only called if using_sprintf/0, so we produce an error
% by default.
error("string.uint64_hex_uc_specifier/0 not defined").
% C does not define the 'p' specifier for uint64_t, so we just treat it as
% hexadecimal here. What that specifier does is implementation defined
% in C anyway.
:- func uint64_hex_p_specifier = string.
uint64_hex_p_specifier =
uint64_hex_lc_specifier.
% Create a string from a float using the format string.
% Note it is the responsibility of the caller to ensure that the
% format string is valid.
%
:- func native_format_float(string, float) = string.
:- pragma no_determinism_warning(native_format_float/2).
:- pragma foreign_proc("C",
native_format_float(FormatStr::in, Val::in) = (Str::out),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness, no_sharing],
"{
MR_save_transient_hp();
Str = MR_make_string(MR_ALLOC_ID, FormatStr, (double) Val);
MR_restore_transient_hp();
}").
native_format_float(_, _) = _ :-
% This predicate is only called if using_sprintf/0, so we produce an error
% by default.
error("string.native_format_float/2 not defined").
% Create a string from a int using the format string.
% Note it is the responsibility of the caller to ensure that the
% format string is valid.
%
:- func native_format_int(string, int) = string.
:- pragma no_determinism_warning(native_format_int/2).
:- pragma foreign_proc("C",
native_format_int(FormatStr::in, Val::in) = (Str::out),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness, no_sharing],
"{
MR_save_transient_hp();
Str = MR_make_string(MR_ALLOC_ID, FormatStr, Val);
MR_restore_transient_hp();
}").
native_format_int(_, _) = _ :-
% This predicate is only called if using_sprintf/0, so we produce an error
% by default.
error("string.native_format_int/2 not defined").
% Create a string from a uint using the format string.
% Note it is the responsibility of the caller to ensure that the
% format string is valid.
%
:- func native_format_uint(string, uint) = string.
:- pragma no_determinism_warning(native_format_uint/2).
:- pragma foreign_proc("C",
native_format_uint(FormatStr::in, Val::in) = (Str::out),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness, no_sharing],
"{
MR_save_transient_hp();
Str = MR_make_string(MR_ALLOC_ID, FormatStr, Val);
MR_restore_transient_hp();
}").
native_format_uint(_, _) = _ :-
% This predicate is only called if using_sprintf/0, so we produce an error
% by default.
error("string.native_format_uint/2 not defined").
:- func native_format_int64(string, int64) = string.
:- pragma no_determinism_warning(native_format_int64/2).
:- pragma foreign_proc("C",
native_format_int64(FormatStr::in, Val::in) = (Str::out),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness, no_sharing],
"{
MR_save_transient_hp();
Str = MR_make_string(MR_ALLOC_ID, FormatStr, Val);
MR_restore_transient_hp();
}").
native_format_int64(_, _) = _ :-
% This predicate is only called if using_sprintf/0, so we produce an error
% by default.
error("string.native_format_int64/2 not defined").
:- func native_format_uint64(string, uint64) = string.
:- pragma no_determinism_warning(native_format_uint64/2).
:- pragma foreign_proc("C",
native_format_uint64(FormatStr::in, Val::in) = (Str::out),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness, no_sharing],
"{
MR_save_transient_hp();
Str = MR_make_string(MR_ALLOC_ID, FormatStr, Val);
MR_restore_transient_hp();
}").
native_format_uint64(_, _) = _ :-
% This predicate is only called if using_sprintf/0, so we produce an error
% by default.
error("string.native_format_uint64/2 not defined").
% Create a string from a string using the format string.
% Note it is the responsibility of the caller to ensure that the
% format string is valid.
%
:- func native_format_string(string, string) = string.
:- pragma no_determinism_warning(native_format_string/2).
:- pragma foreign_proc("C",
native_format_string(FormatStr::in, Val::in) = (Str::out),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness, no_sharing],
"{
MR_save_transient_hp();
Str = MR_make_string(MR_ALLOC_ID, FormatStr, Val);
MR_restore_transient_hp();
}").
native_format_string(_, _) = _ :-
% This predicate is only called if using_sprintf/0, so we produce an error
% by default.
error("string.native_format_string/2 not defined").
% Create a string from a char using the format string.
% Note it is the responsibility of the caller to ensure that the
% format string is valid.
%
:- func native_format_char(string, char) = string.
:- pragma no_determinism_warning(native_format_char/2).
:- pragma foreign_proc("C",
native_format_char(FormatStr::in, Val::in) = (Str::out),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness, no_sharing],
"{
MR_save_transient_hp();
Str = MR_make_string(MR_ALLOC_ID, FormatStr, Val);
MR_restore_transient_hp();
}").
native_format_char(_, _) = _ :-
% This predicate is only called if using_sprintf/0, so we produce an error
% by default.
error("string.native_format_char/2 not defined").
%---------------------------------------------------------------------------%
% Format a character.
%
:- func format_char(string_format_flags, string_format_maybe_width, char)
= string.
format_char(Flags, MaybeWidth, Char) = String :-
CharStr = string.char_to_string(Char),
String = justify_string(Flags, MaybeWidth, CharStr).
%---------------------------------------------------------------------------%
% Format a string.
%
:- func format_string(string_format_flags, string_format_maybe_width,
string_format_maybe_prec, string) = string.
format_string(Flags, MaybeWidth, MaybePrec, OldStr) = NewStr :-
(
MaybePrec = specified_prec(NumChars),
PrecStr = string.left_by_codepoint(OldStr, NumChars)
;
MaybePrec = no_specified_prec,
PrecStr = OldStr
),
NewStr = justify_string(Flags, MaybeWidth, PrecStr).
%---------------------------------------------------------------------------%
% Format an int (as a signed value).
%
:- func format_signed_int(string_format_flags, string_format_maybe_width,
string_format_maybe_prec, int) = string.
format_signed_int(Flags, MaybeWidth, MaybePrec, Int) = String :-
( if Int = 0 then
% Zero is a special case. The abs_integer_to_decimal function
% returns "" for 0, but returning no digits at all is ok
% only if our caller explicitly allowed us to do so.
( if MaybePrec = specified_prec(0) then
AbsIntStr = ""
else
AbsIntStr = "0"
)
else
% In the general case, we use the arbitrary-precision arithmetic
% of integer.m to print integers. This is needed to print
% the most negative number of our platform, whose absolute value
% cannot be represented natively on machines with two's complement
% integers.
% However, if the absolute value of the integer we want to print
% *does* fit into a native int, we want to work on ints, not integers.
% We assume that our platform has at least 32 bit ints; note that
% 2147483647 = 2^31-1. The "absolute value of" part of the above
% is why the test is designed to fail for -2147483648.
( if -2147483647 =< Int, Int =< 2147483647 then
AbsInt = int.abs(Int),
AbsIntStr = abs_int_to_decimal(AbsInt)
else
AbsInteger = integer.abs(integer(Int)),
AbsIntStr = abs_integer_to_decimal(AbsInteger)
)
),
AbsIntStrLength = string.count_codepoints(AbsIntStr),
% Do we need to increase precision?
( if
MaybePrec = specified_prec(Prec),
Prec > AbsIntStrLength
then
PrecStr = string.pad_left(AbsIntStr, '0', Prec)
else
PrecStr = AbsIntStr
),
% Do we need to pad to the field width?
( if
MaybeWidth = specified_width(Width),
Width > string.count_codepoints(PrecStr),
Flags ^ flag_zero = flag_zero_set,
Flags ^ flag_minus = flag_minus_clear,
MaybePrec = no_specified_prec
then
FieldStr = string.pad_left(PrecStr, '0', Width - 1),
ZeroPadded = yes
else
FieldStr = PrecStr,
ZeroPadded = no
),
% Prefix with appropriate sign or zero padding.
% The previous step has deliberately left room for this.
SignedStr = add_sign_like_prefix_to_int_if_needed(Flags, ZeroPadded, Int,
FieldStr),
String = justify_string(Flags, MaybeWidth, SignedStr).
%---------------------------------------------------------------------------%
% Format a uint.
% Also used for formatting ints as unsigned values.
%
:- func format_uint(string_format_flags, string_format_maybe_width,
string_format_maybe_prec, string_format_int_base, uint) = string.
format_uint(Flags, MaybeWidth, MaybePrec, Base, UInt) = String :-
( if UInt = 0u then
% Zero is a special case, the uint_to_*string functions return "0" for
% 0, but we must return "" if our caller explicitly allowed us to do
% so.
( if MaybePrec = specified_prec(0) then
UIntStr = ""
else
UIntStr = "0"
)
else
(
Base = base_octal,
UIntStr = uint_to_octal_string(UInt)
;
Base = base_decimal,
UIntStr = uint_to_string(UInt)
;
( Base = base_hex_lc
; Base = base_hex_p
),
UIntStr = uint_to_lc_hex_string(UInt)
;
Base = base_hex_uc,
UIntStr = uint_to_uc_hex_string(UInt)
)
),
UIntStrLength = string.count_codepoints(UIntStr),
% Do we need to increase precision?
( if
MaybePrec = specified_prec(Prec),
Prec > UIntStrLength
then
PrecStr = string.pad_left(UIntStr, '0', Prec)
else
PrecStr = UIntStr
),
% Do we need to increase the precision of an octal?
( if
Base = base_octal,
Flags ^ flag_hash = flag_hash_set,
not string.prefix(PrecStr, "0")
then
PrecModStr = "0" ++ PrecStr
else
PrecModStr = PrecStr
),
% Do we need to pad to the field width?
( if
MaybeWidth = specified_width(Width),
Width > string.count_codepoints(PrecModStr),
Flags ^ flag_zero = flag_zero_set,
Flags ^ flag_minus = flag_minus_clear,
MaybePrec = no_specified_prec
then
% Do we need to make room for "0x" or "0X" ?
( if
Flags ^ flag_hash = flag_hash_set,
require_complete_switch [Base]
(
Base = base_hex_p,
Prefix = "0x"
;
Base = base_hex_lc,
UInt \= 0u,
Prefix = "0x"
;
Base = base_hex_uc,
UInt \= 0u,
Prefix = "0X"
;
( Base = base_octal
; Base = base_decimal
),
% These get padded with just zeroes on the left.
fail
)
then
FieldStr = string.pad_left(PrecModStr, '0', Width - 2),
FieldModStr = Prefix ++ FieldStr
else
FieldStr = string.pad_left(PrecModStr, '0', Width),
FieldModStr = FieldStr
)
else
FieldStr = PrecModStr,
% Do we have to prefix "0x" or "0X"?
( if
Flags ^ flag_hash = flag_hash_set,
require_complete_switch [Base]
(
Base = base_hex_p,
Prefix = "0x"
;
Base = base_hex_lc,
UInt \= 0u,
Prefix = "0x"
;
Base = base_hex_uc,
UInt \= 0u,
Prefix = "0X"
;
Base = base_octal,
% We took care of adding the "0" prefix above.
fail
;
Base = base_decimal,
fail
)
then
FieldModStr = Prefix ++ FieldStr
else
FieldModStr = FieldStr
)
),
String = justify_string(Flags, MaybeWidth, FieldModStr).
%---------------------------------------------------------------------------%
% Format an int64 (as a signed value).
%
:- func format_signed_int64(string_format_flags, string_format_maybe_width,
string_format_maybe_prec, int64) = string.
format_signed_int64(Flags, MaybeWidth, MaybePrec, Int) = String :-
( if Int = 0i64 then
% Zero is a special case, int64_to_string returns "0" for 0, but we
% must return "" only if our caller explicitly allowed us to do so.
( if MaybePrec = specified_prec(0) then
AbsIntStr = ""
else
AbsIntStr = "0"
)
else if Int = int64.min_int64 then
AbsIntStr = "9223372036854775808"
else
AbsInt = int64.unchecked_abs(Int),
AbsIntStr = int64_to_string(AbsInt)
),
AbsIntStrLength = string.count_codepoints(AbsIntStr),
% Do we need to increase precision?
( if
MaybePrec = specified_prec(Prec),
Prec > AbsIntStrLength
then
PrecStr = string.pad_left(AbsIntStr, '0', Prec)
else
PrecStr = AbsIntStr
),
% Do we need to pad to the field width?
( if
MaybeWidth = specified_width(Width),
Width > string.count_codepoints(PrecStr),
Flags ^ flag_zero = flag_zero_set,
Flags ^ flag_minus = flag_minus_clear,
MaybePrec = no_specified_prec
then
FieldStr = string.pad_left(PrecStr, '0', Width - 1),
ZeroPadded = yes
else
FieldStr = PrecStr,
ZeroPadded = no
),
% Prefix with appropriate sign or zero padding.
% The previous step has deliberately left room for this.
SignedStr = add_sign_like_prefix_to_int64_if_needed(Flags, ZeroPadded, Int,
FieldStr),
String = justify_string(Flags, MaybeWidth, SignedStr).
%---------------------------------------------------------------------------%
% Format a uint64.
% Also used for formatting int64s as unsigned values.
%
:- func format_uint64(string_format_flags, string_format_maybe_width,
string_format_maybe_prec, string_format_int_base, uint64) = string.
format_uint64(Flags, MaybeWidth, MaybePrec, Base, UInt64) = String :-
( if UInt64 = 0u64 then
% Zero is a special case, the uint64_to_*string functions return "0"
% for 0, but we must return "" only if our caller explicitly allowed us
% to do so.
( if MaybePrec = specified_prec(0) then
UInt64Str = ""
else
UInt64Str = "0"
)
else
(
Base = base_octal,
UInt64Str = uint64_to_octal_string(UInt64)
;
Base = base_decimal,
UInt64Str = uint64_to_string(UInt64)
;
( Base = base_hex_lc
; Base = base_hex_p
),
UInt64Str = uint64_to_lc_hex_string(UInt64)
;
Base = base_hex_uc,
UInt64Str = uint64_to_uc_hex_string(UInt64)
)
),
UInt64StrLength = string.count_codepoints(UInt64Str),
% Do we need to increase precision?
( if
MaybePrec = specified_prec(Prec),
Prec > UInt64StrLength
then
PrecStr = string.pad_left(UInt64Str, '0', Prec)
else
PrecStr = UInt64Str
),
% Do we need to increase the precision of an octal?
( if
Base = base_octal,
Flags ^ flag_hash = flag_hash_set,
not string.prefix(PrecStr, "0")
then
PrecModStr = "0" ++ PrecStr
else
PrecModStr = PrecStr
),
% Do we need to pad to the field width?
( if
MaybeWidth = specified_width(Width),
Width > string.count_codepoints(PrecModStr),
Flags ^ flag_zero = flag_zero_set,
Flags ^ flag_minus = flag_minus_clear,
MaybePrec = no_specified_prec
then
% Do we need to make room for "0x" or "0X" ?
( if
Flags ^ flag_hash = flag_hash_set,
require_complete_switch [Base]
(
Base = base_hex_p,
Prefix = "0x"
;
Base = base_hex_lc,
UInt64 \= 0u64,
Prefix = "0x"
;
Base = base_hex_uc,
UInt64 \= 0u64,
Prefix = "0X"
;
( Base = base_octal
; Base = base_decimal
),
% These get padded with just zeroes on the left.
fail
)
then
FieldStr = string.pad_left(PrecModStr, '0', Width - 2),
FieldModStr = Prefix ++ FieldStr
else
FieldStr = string.pad_left(PrecModStr, '0', Width),
FieldModStr = FieldStr
)
else
FieldStr = PrecModStr,
% Do we have to prefix "0x" or "0X"?
( if
Flags ^ flag_hash = flag_hash_set,
require_complete_switch [Base]
(
Base = base_hex_p,
Prefix = "0x"
;
Base = base_hex_lc,
UInt64 \= 0u64,
Prefix = "0x"
;
Base = base_hex_uc,
UInt64 \= 0u64,
Prefix = "0X"
;
Base = base_octal,
% We took care of adding the "0" prefix above.
fail
;
Base = base_decimal,
fail
)
then
FieldModStr = Prefix ++ FieldStr
else
FieldModStr = FieldStr
)
),
String = justify_string(Flags, MaybeWidth, FieldModStr).
%---------------------------------------------------------------------------%
% Format a float.
%
:- func format_float(string_format_flags, string_format_maybe_width,
string_format_maybe_prec, string_format_float_kind, float) = string.
format_float(Flags, MaybeWidth, MaybePrec, Kind, Float) = String :-
( if is_nan(Float) then
(
( Kind = kind_e_scientific_lc
; Kind = kind_f_plain_lc
; Kind = kind_g_flexible_lc
),
SignedStr = "nan"
;
( Kind = kind_e_scientific_uc
; Kind = kind_f_plain_uc
; Kind = kind_g_flexible_uc
),
SignedStr = "NAN"
)
else if is_infinite(Float) then
(
( Kind = kind_e_scientific_lc
; Kind = kind_f_plain_lc
; Kind = kind_g_flexible_lc
),
SignedStr = ( if Float < 0.0 then "-infinity" else "infinity" )
;
( Kind = kind_e_scientific_uc
; Kind = kind_f_plain_uc
; Kind = kind_g_flexible_uc
),
SignedStr = ( if Float < 0.0 then "-INFINITY" else "INFINITY" )
)
else
% XXX This general approach of converting the float to a string
% using convert_float_to_string and then post-processing it
% is far from ideal, since it is significantly less efficient
% than building the right string directly.
AbsFloat = abs(Float),
AbsStr = convert_float_to_string(AbsFloat),
% Change precision if needed.
(
( Kind = kind_e_scientific_lc, E = "e"
; Kind = kind_e_scientific_uc, E = "E"
),
Prec = get_prec_to_use(MaybePrec),
PrecStr = change_to_e_notation(AbsStr, Prec, E),
% Do we need to remove the decimal point?
( if
Flags ^ flag_hash = flag_hash_clear,
MaybePrec = specified_prec(0)
then
split_at_decimal_point(PrecStr, BaseStr, ExponentStr),
PrecModStr = BaseStr ++ ExponentStr
else
PrecModStr = PrecStr
)
;
( Kind = kind_f_plain_lc
; Kind = kind_f_plain_uc
),
Prec = get_prec_to_use(MaybePrec),
PrecStr = change_precision(AbsStr, Prec),
% Do we need to remove the decimal point?
( if
Flags ^ flag_hash = flag_hash_clear,
MaybePrec = specified_prec(0)
then
PrecStrLen = string.count_codepoints(PrecStr),
PrecModStr = string.between(PrecStr, 0, PrecStrLen - 1)
else
PrecModStr = PrecStr
)
;
( Kind = kind_g_flexible_lc, E = "e"
; Kind = kind_g_flexible_uc, E = "E"
),
Prec = get_prec_to_use_minimum_1(MaybePrec),
PrecStr = change_to_g_notation(AbsStr, Prec, E, Flags),
% Don't ever remove the decimal point.
% XXX Why? Does change_to_g_notation do it?
PrecModStr = PrecStr
),
% Do we need to change field width?
( if
MaybeWidth = specified_width(Width),
Width > string.count_codepoints(PrecModStr),
Flags ^ flag_zero = flag_zero_set,
Flags ^ flag_minus = flag_minus_clear
then
FieldStr = string.pad_left(PrecModStr, '0', Width - 1),
ZeroPadded = yes
else
FieldStr = PrecModStr,
ZeroPadded = no
),
% Finishing up.
SignedStr = add_sign_like_prefix_to_float_if_needed(Flags, ZeroPadded,
Float, FieldStr)
),
String = justify_string(Flags, MaybeWidth, SignedStr).
%---------------------------------------------------------------------------%
:- func get_prec_to_use(string_format_maybe_prec) = int.
:- pragma inline(get_prec_to_use/1).
get_prec_to_use(MaybePrec) = Prec :-
(
MaybePrec = specified_prec(Prec)
;
MaybePrec = no_specified_prec,
% The default precision is 6.
Prec = 6
).
:- func get_prec_to_use_minimum_1(string_format_maybe_prec) = int.
:- pragma inline(get_prec_to_use_minimum_1/1).
get_prec_to_use_minimum_1(MaybePrec) = Prec :-
(
MaybePrec = specified_prec(Prec0),
( if Prec0 = 0 then
Prec = 1
else
Prec = Prec0
)
;
MaybePrec = no_specified_prec,
% The default precision is 6.
Prec = 6
).
%---------------------------------------------------------------------------%
:- func add_sign_like_prefix_to_int_if_needed(string_format_flags, bool,
int, string) = string.
:- pragma inline(add_sign_like_prefix_to_int_if_needed/4).
add_sign_like_prefix_to_int_if_needed(Flags, ZeroPadded, Int, FieldStr)
= SignedStr :-
( if Int < 0 then
SignedStr = "-" ++ FieldStr
else if Flags ^ flag_plus = flag_plus_set then
SignedStr = "+" ++ FieldStr
else if Flags ^ flag_space = flag_space_set then
SignedStr = " " ++ FieldStr
else
(
ZeroPadded = yes,
SignedStr = "0" ++ FieldStr
;
ZeroPadded = no,
SignedStr = FieldStr
)
).
:- func add_sign_like_prefix_to_int64_if_needed(string_format_flags, bool,
int64, string) = string.
:- pragma inline(add_sign_like_prefix_to_int64_if_needed/4).
add_sign_like_prefix_to_int64_if_needed(Flags, ZeroPadded, Int64, FieldStr)
= SignedStr :-
( if int64.(Int64 < 0i64) then
SignedStr = "-" ++ FieldStr
else if Flags ^ flag_plus = flag_plus_set then
SignedStr = "+" ++ FieldStr
else if Flags ^ flag_space = flag_space_set then
SignedStr = " " ++ FieldStr
else
(
ZeroPadded = yes,
SignedStr = "0" ++ FieldStr
;
ZeroPadded = no,
SignedStr = FieldStr
)
).
:- func add_sign_like_prefix_to_float_if_needed(string_format_flags, bool,
float, string) = string.
:- pragma inline(add_sign_like_prefix_to_float_if_needed/4).
add_sign_like_prefix_to_float_if_needed(Flags, ZeroPadded, Float, FieldStr)
= SignedStr :-
% XXX Float < 0.0 is the wrong test, because it fails for -0.0.
% We should test the sign bit instead. This can be done using
% signbit(Float) in C, but I (zs) don't know its equivalents
% for the other backends.
( if Float < 0.0 then
SignedStr = "-" ++ FieldStr
else if Flags ^ flag_plus = flag_plus_set then
SignedStr = "+" ++ FieldStr
else if Flags ^ flag_space = flag_space_set then
SignedStr = " " ++ FieldStr
else
(
ZeroPadded = yes,
SignedStr = "0" ++ FieldStr
;
ZeroPadded = no,
SignedStr = FieldStr
)
).
:- func justify_string(string_format_flags, string_format_maybe_width,
string) = string.
justify_string(Flags, MaybeWidth, Str) = JustifiedStr :-
( if
MaybeWidth = specified_width(Width),
Width > string.count_codepoints(Str)
then
( if Flags ^ flag_minus = flag_minus_set then
string.pad_right(Str, ' ', Width, JustifiedStr)
else
string.pad_left(Str, ' ', Width, JustifiedStr)
)
else
JustifiedStr = Str
).
%---------------------------------------------------------------------------%
%
% Each of these functions converts a non-negative integer (that originally
% came from a Mercury int) to a string of octal, decimal or hex digits.
%
% The input is an arbitrary precision integer because if either
%
% - the original number is a signed int, and its value is min_int, or
% - the original number is an unsigned int, and its value has the most
% significant bit set,
%
% then the absolute value of that number cannot be represented as
% a Mercury int, which is always signed and always word-sized. However,
% once we have divided the original integer by 8, 10 or 16, the result
% is guaranteed not to suffer from either of the problems above,
% so we process it as an Mercury int, which is a lot faster.
% Convert a non-negative integer to a decimal string.
%
:- func abs_integer_to_decimal(integer) = string.
:- func abs_int_to_decimal(int) = string.
abs_integer_to_decimal(Num) = NumStr :-
( if Num > integer.zero then
Integer10 = integer.ten,
FrontDigitsStr = abs_int_to_decimal(det_to_int(Num // Integer10)),
LastDigitStr = get_decimal_digit(det_to_int(Num rem Integer10)),
NumStr = append(FrontDigitsStr, LastDigitStr)
else
NumStr = ""
).
abs_int_to_decimal(Num) = NumStr :-
( if Num > 0 then
FrontDigitsStr = abs_int_to_decimal(Num // 10),
LastDigitStr = get_decimal_digit(Num rem 10),
NumStr = append(FrontDigitsStr, LastDigitStr)
else
NumStr = ""
).
%---------------------------------------------------------------------------%
% Given an int between 0 and 9, return the decimal digit representing it.
%
:- func get_decimal_digit(int) = string.
:- pragma inline(get_decimal_digit/1).
get_decimal_digit(Int) = Decimal :-
( if decimal_digit(Int, DecimalPrime) then
Decimal = DecimalPrime
else
unexpected($pred, "decimal_digit failed")
).
:- pred decimal_digit(int::in, string::out) is semidet.
decimal_digit(0, "0").
decimal_digit(1, "1").
decimal_digit(2, "2").
decimal_digit(3, "3").
decimal_digit(4, "4").
decimal_digit(5, "5").
decimal_digit(6, "6").
decimal_digit(7, "7").
decimal_digit(8, "8").
decimal_digit(9, "9").
%---------------------------------------------------------------------------%
% Unlike the standard library function, this function converts a float
% to a string without resorting to scientific notation.
%
% This predicate relies on the fact that string.float_to_string returns
% a float which is round-trippable, ie to the full precision needed.
%
:- func convert_float_to_string(float) = string.
convert_float_to_string(Float) = String :-
float_to_string_first_pass(Float, FloatStr),
% Check for scientific representation.
( if
( string.contains_char(FloatStr, 'e')
; string.contains_char(FloatStr, 'E')
)
then
split_at_exponent(FloatStr, FloatPtStr, ExpStr),
split_at_decimal_point(FloatPtStr, MantissaStr, FractionStr),
% What is the exponent?
ExpInt = string.det_to_int(ExpStr),
( if ExpInt >= 0 then
% Move decimal pt to the right.
ExtraDigits = ExpInt,
PaddedFracStr = string.pad_right(FractionStr, '0', ExtraDigits),
string.split(PaddedFracStr, ExtraDigits, MantissaRest,
NewFraction),
NewMantissa = MantissaStr ++ MantissaRest,
MantAndPoint = NewMantissa ++ ".",
( if NewFraction = "" then
String = MantAndPoint ++ "0"
else
String = MantAndPoint ++ NewFraction
)
else
% Move decimal pt to the left.
ExtraDigits = abs(ExpInt),
PaddedMantissaStr = string.pad_left(MantissaStr, '0',
ExtraDigits),
string.split(PaddedMantissaStr,
length(PaddedMantissaStr) - ExtraDigits,
NewMantissa, FractionRest),
( if NewMantissa = "" then
MantAndPoint = "0."
else
MantAndPoint = NewMantissa ++ "."
),
String = MantAndPoint ++ FractionRest ++ FractionStr
)
else
String = FloatStr
).
% float_to_string_first_pass differs from string.float_to_string in that
% it must be implemented without calling string.format, as this is the
% predicate that the implementation of string.format uses to get
% the initial string representation of a float.
%
% The string returned must match one of the following regular expression:
% ^[+-]?[0-9]*\.?[0-9]+((e|E)[0-9]+)?$
% ^[nN][aA][nN]$
% ^[+-]?[iI][nN][fF][iI][nN][iI][tT][yY]$
% ^[+-]?[iI][nN][fF]$
% and the string returned must have sufficient precision for representing
% the float.
%
:- pred float_to_string_first_pass(float::in, string::uo) is det.
:- pragma foreign_proc("C",
float_to_string_first_pass(FloatVal::in, FloatString::uo),
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness, no_sharing],
"{
// Note any changes here will require the same changes in
// string.float_to_string.
MR_float_to_string(FloatVal, FloatString, MR_ALLOC_ID);
}").
:- pragma foreign_proc("C#",
float_to_string_first_pass(FloatVal::in, FloatString::uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
// The R format string prints the double out such that it can be
// round-tripped.
// XXX According to the documentation it tries 15 digits of precision,
// then 17 digits skipping 16 digits of precision, unlike what we do
// for the C backend.
FloatString = FloatVal.ToString(""R"");
").
:- pragma foreign_proc("Java",
float_to_string_first_pass(FloatVal::in, FloatString::uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
FloatString = java.lang.Double.toString(FloatVal);
").
% Converts a floating point number to a specified number of standard
% figures. The style used depends on the value converted; style e (or E)
% is used only if the exponent resulting from such a conversion is less
% than -4 or greater than or equal to the precision. Trailing zeros are
% removed from the fractional portion of the result unless the # flag
% is specified: a decimal-point character appears only if it is followed
% by a digit.
%
:- func change_to_g_notation(string, int, string, string_format_flags)
= string.
change_to_g_notation(Float, Prec, E, Flags) = FormattedFloat :-
Exponent = size_of_required_exponent(Float, Prec),
( if
Exponent >= -4,
Exponent < Prec
then
% Float will be represented normally.
% -----------------------------------
% We need to calculate precision to pass to change_precision,
% because the current precision represents significant figures,
% not decimal places.
%
% Now change float's precision.
%
( if Exponent =< 0 then
% Deal with floats such as 0.00000000xyz.
DecimalPos = decimal_pos(Float),
FormattedFloat0 = change_precision(Float,
abs(DecimalPos) - 1 + Prec)
else
% Deal with floats such as ddddddd.mmmmmmmm.
ScientificFloat = change_to_e_notation(Float, Prec - 1, "e"),
split_at_exponent(ScientificFloat, BaseStr, ExponentStr),
Exp = string.det_to_int(ExponentStr),
split_at_decimal_point(BaseStr, MantissaStr, FractionStr),
RestMantissaStr = between(FractionStr, 0, Exp),
NewFraction = between(FractionStr, Exp, Prec - 1),
FormattedFloat0 = MantissaStr ++ RestMantissaStr
++ "." ++ NewFraction
),
% Do we remove trailing zeros?
( if Flags ^ flag_hash = flag_hash_set then
FormattedFloat = FormattedFloat0
else
FormattedFloat = remove_trailing_zeros(FormattedFloat0)
)
else
% Float will be represented in scientific notation.
% -------------------------------------------------
UncheckedFloat = change_to_e_notation(Float, Prec - 1, E),
% Do we need to remove trailing zeros?
( if Flags ^ flag_hash = flag_hash_set then
FormattedFloat = UncheckedFloat
else
split_at_exponent(UncheckedFloat, BaseStr, ExponentStr),
NewBaseStr = remove_trailing_zeros(BaseStr),
FormattedFloat = NewBaseStr ++ E ++ ExponentStr
)
).
% Convert floating point notation to scientific notation.
%
:- func change_to_e_notation(string, int, string) = string.
change_to_e_notation(Float, Prec, E) = ScientificFloat :-
UnsafeExponent = decimal_pos(Float),
UnsafeBase = calculate_base_unsafe(Float, Prec),
% Is mantissa greater than one digit long?
split_at_decimal_point(UnsafeBase, MantissaStr, _FractionStr),
( if string.count_codepoints(MantissaStr) > 1 then
% Need to append 0, to fix the problem of having no numbers
% after the decimal point.
SafeBase = calculate_base_unsafe(string.append(UnsafeBase, "0"),
Prec),
SafeExponent = UnsafeExponent + 1
else
SafeBase = UnsafeBase,
SafeExponent = UnsafeExponent
),
% Creating exponent.
( if SafeExponent >= 0 then
( if SafeExponent < 10 then
ExponentStr = string.append_list(
[E, "+0", string.int_to_string(SafeExponent)])
else
ExponentStr = string.append_list(
[E, "+", string.int_to_string(SafeExponent)])
)
else
( if SafeExponent > -10 then
ExponentStr = string.append_list(
[E, "-0", string.int_to_string(int.abs(SafeExponent))])
else
ExponentStr = E ++ string.int_to_string(SafeExponent)
)
),
ScientificFloat = SafeBase ++ ExponentStr.
% Given a floating point number, this function calculates the size of
% the exponent needed to represent the float in scientific notation.
%
:- func size_of_required_exponent(string, int) = int.
size_of_required_exponent(Float, Prec) = Exponent :-
UnsafeExponent = decimal_pos(Float),
UnsafeBase = calculate_base_unsafe(Float, Prec),
% Is mantissa one digit long?
split_at_decimal_point(UnsafeBase, MantissaStr, _FractionStr),
( if string.count_codepoints(MantissaStr) > 1 then
% We will need to move decimal pt one place to the left:
% therefore, increment exponent.
Exponent = UnsafeExponent + 1
else
Exponent = UnsafeExponent
).
% Given a string representing a floating point number, this function
% returns the string with all its trailing zeros removed.
%
:- func remove_trailing_zeros(string) = string.
remove_trailing_zeros(Float) = TrimmedFloat :-
FloatCharList = string.to_char_list(Float),
FloatCharListRev = list.reverse(FloatCharList),
TrimmedFloatRevCharList = remove_zeros(FloatCharListRev),
TrimmedFloatCharList = list.reverse(TrimmedFloatRevCharList),
TrimmedFloat = string.from_char_list(TrimmedFloatCharList).
% Given a char list, this function removes all leading zeros,
% including the decimal point, if need be.
%
:- func remove_zeros(list(char)) = list(char).
remove_zeros(CharNum) = TrimmedNum :-
( if CharNum = ['0' | Rest] then
TrimmedNum = remove_zeros(Rest)
else if CharNum = ['.' | Rest] then
TrimmedNum = Rest
else
TrimmedNum = CharNum
).
% Determine the location of the decimal point in a string
% that represents a floating point number.
%
:- func decimal_pos(string) = int.
decimal_pos(Float) = Pos :-
split_at_decimal_point(Float, MantissaStr, _FractionStr),
NumZeros = string.count_codepoints(MantissaStr) - 1,
Pos = find_non_zero_pos(string.to_char_list(Float), NumZeros).
% Given a list of chars representing a floating point number, this
% function determines the first position containing a non-zero digit.
% Positions after the decimal point are negative, and those before the
% decimal point are positive.
%
:- func find_non_zero_pos(list(char), int) = int.
find_non_zero_pos(L, CurrentPos) = ActualPos :-
(
L = [H | T],
( if is_decimal_point(H) then
ActualPos = find_non_zero_pos(T, CurrentPos)
else if H = '0' then
ActualPos = find_non_zero_pos(T, CurrentPos - 1)
else
ActualPos = CurrentPos
)
;
L = [],
ActualPos = 0
).
% Representing a floating point number in scientific notation requires
% a base and an exponent. This function returns the base.
% But it is unsafe, because particular inputs can result in the base
% having a mantissa with more than one digit. Therefore, the calling
% function must check for this problem.
%
:- func calculate_base_unsafe(string, int) = string.
calculate_base_unsafe(Float, Prec) = Exp :-
Place = decimal_pos(Float),
split_at_decimal_point(Float, MantissaStr, FractionStr),
( if Place < 0 then
DecimalPos = abs(Place),
PaddedMantissaStr = string.between(FractionStr, 0, DecimalPos),
% Get rid of superfluous zeros.
MantissaInt = string.det_to_int(PaddedMantissaStr),
ExpMantissaStr = string.int_to_string(MantissaInt),
% Create fractional part.
PaddedFractionStr = pad_right(FractionStr, '0', Prec + 1),
ExpFractionStr = string.between(PaddedFractionStr, DecimalPos,
DecimalPos + Prec + 1)
else if Place > 0 then
ExpMantissaStr = string.between(MantissaStr, 0, 1),
FirstHalfOfFractionStr = string.between(MantissaStr, 1, Place + 1),
ExpFractionStr = FirstHalfOfFractionStr ++ FractionStr
else
ExpMantissaStr = MantissaStr,
ExpFractionStr = FractionStr
),
MantissaAndPoint = ExpMantissaStr ++ ".",
UnroundedExpStr = MantissaAndPoint ++ ExpFractionStr,
Exp = change_precision(UnroundedExpStr, Prec).
% Change the precision of a float to a specified number of decimal places.
%
% n.b. OldFloat must be positive for this function to work.
%
:- func change_precision(string, int) = string.
change_precision(OldFloat, Prec) = NewFloat :-
split_at_decimal_point(OldFloat, MantissaStr, FractionStr),
FracStrLen = string.count_codepoints(FractionStr),
( if Prec > FracStrLen then
PrecFracStr = string.pad_right(FractionStr, '0', Prec),
PrecMantissaStr = MantissaStr
else if Prec < FracStrLen then
UnroundedFrac = string.between(FractionStr, 0, Prec),
NextDigit = string.det_index(FractionStr, Prec),
( if
UnroundedFrac \= "",
(char.to_int(NextDigit) - char.to_int('0')) >= 5
then
NewPrecFrac = string.det_to_int(UnroundedFrac) + 1,
NewPrecFracStrNotOK = string.int_to_string( NewPrecFrac),
NewPrecFracStr = string.pad_left(NewPrecFracStrNotOK, '0', Prec),
( if
string.count_codepoints(NewPrecFracStr) >
string.count_codepoints(UnroundedFrac)
then
PrecFracStr = between(NewPrecFracStr, 1, 1 + Prec),
PrecMantissaInt = det_to_int(MantissaStr) + 1,
PrecMantissaStr = int_to_string(PrecMantissaInt)
else
PrecFracStr = NewPrecFracStr,
PrecMantissaStr = MantissaStr
)
else if
UnroundedFrac = "",
(char.to_int(NextDigit) - char.to_int('0')) >= 5
then
PrecMantissaInt = det_to_int(MantissaStr) + 1,
PrecMantissaStr = int_to_string(PrecMantissaInt),
PrecFracStr = ""
else
PrecFracStr = UnroundedFrac,
PrecMantissaStr = MantissaStr
)
else
PrecFracStr = FractionStr,
PrecMantissaStr = MantissaStr
),
HalfNewFloat = PrecMantissaStr ++ ".",
NewFloat = HalfNewFloat ++ PrecFracStr.
:- pred split_at_exponent(string::in, string::out, string::out) is det.
split_at_exponent(Str, Float, Exponent) :-
FloatAndExponent = string.words_separator(is_exponent, Str),
list.det_index0(FloatAndExponent, 0, Float),
list.det_index0(FloatAndExponent, 1, Exponent).
:- pred split_at_decimal_point(string::in, string::out, string::out) is det.
split_at_decimal_point(Str, Mantissa, Fraction) :-
MantAndFrac = string.words_separator(is_decimal_point, Str),
list.det_index0(MantAndFrac, 0, Mantissa),
( if list.index0(MantAndFrac, 1, Fraction0) then
Fraction = Fraction0
else
Fraction = ""
).
:- pred is_decimal_point(char::in) is semidet.
is_decimal_point('.').
:- pred is_exponent(char::in) is semidet.
is_exponent('e').
is_exponent('E').
%---------------------------------------------------------------------------%
:- func sized_int_to_int(sized_int) = int.
sized_int_to_int(SizedInt) = Int :-
(
SizedInt = sized_int(Int)
;
SizedInt = sized_int8(Int8),
format_cast_int8_to_int(Int8, Int)
;
SizedInt = sized_int16(Int16),
format_cast_int16_to_int(Int16, Int)
;
SizedInt = sized_int32(Int32),
format_cast_int32_to_int(Int32, Int)
;
SizedInt = sized_int64(_),
throw(software_error("formatting int64 via a cast"))
).
:- func sized_uint_to_uint(sized_uint) = uint.
sized_uint_to_uint(SizedUInt) = UInt :-
(
SizedUInt = sized_uint(UInt)
;
SizedUInt = sized_uint8(UInt8),
format_cast_uint8_to_uint(UInt8, UInt)
;
SizedUInt = sized_uint16(UInt16),
format_cast_uint16_to_uint(UInt16, UInt)
;
SizedUInt = sized_uint32(UInt32),
format_cast_uint32_to_uint(UInt32, UInt)
;
SizedUInt = sized_uint64(_),
throw(software_error("formatting uint64 via a cast"))
).
format_cast_int8_to_int(Int8, Int) :-
Int = int8.cast_to_int(Int8).
format_cast_int16_to_int(Int16, Int) :-
Int = int16.cast_to_int(Int16).
format_cast_int32_to_int(Int32, Int) :-
Int = int32.cast_to_int(Int32).
format_cast_uint8_to_uint(UInt8, UInt) :-
UInt = uint8.cast_to_uint(UInt8).
format_cast_uint16_to_uint(UInt16, UInt) :-
UInt = uint16.cast_to_uint(UInt16).
format_cast_uint32_to_uint(UInt32, UInt) :-
UInt = uint32.cast_to_uint(UInt32).
%---------------------------------------------------------------------------%