浏览代码

ft: 修改为基于otp23.2.4编写

master
SisMaker 4 年前
父节点
当前提交
d29937e5e1
共有 2 个文件被更改,包括 122 次插入130 次删除
  1. +14
    -0
      src/eFmt.erl
  2. +108
    -130
      src/eFmtFormat.erl

+ 14
- 0
src/eFmt.erl 查看文件

@ -67,6 +67,17 @@
, chars_length/1
]).
-record(fmtSpec, {
ctlChar :: char() %% $p $w
, args :: [any()] %% 使
, width :: 'none' | integer() %%
, adjust :: 'left' | 'right' %%
, precision :: 'none' | integer() %%
, padChar :: char() %%
, encoding :: 'unicode' | 'latin1' %% ttrue
, strings :: boolean() %% l string设置为false
}).
-export_type([
chars/0
, latin1_string/0
@ -74,6 +85,7 @@
, fread_error/0
, fread_item/0
, format_spec/0
, fmtSpec/0
, chars_limit/0
]).
@ -105,6 +117,8 @@
integer() |
float().
-type fmtSpec() :: #fmtSpec{}.
-type format_spec() ::
#{
control_char := char(),

+ 108
- 130
src/eFmtFormat.erl 查看文件

@ -12,55 +12,39 @@
, build/2
]).
%% Format the arguments in Args after string Format. Just generate
%% an error if there is an error in the arguments.
%% Args
%%
%%
%% To do the printing command correctly we need to calculate the
%% current indentation for everything before it. This may be very
%% expensive, especially when it is not needed, so we first determine
%% if, and for how long, we need to calculate the indentations. We do
%% this by first collecting all the control sequences and
%% corresponding arguments, then counting the print sequences and
%% then building the output. This method has some drawbacks, it does
%% two passes over the format string and creates more temporary data,
%% and it also splits the handling of the control characters into two
%% parts.
-spec fwrite(Format, Data) -> io_lib:chars() when
Format :: io:format(),
Data :: [term()].
%%
%%
%%
%%
%%
%%
%%
%%
%%
%%
-spec fwrite(Format :: io:format(), Data :: [term()]) -> eFmt:chars().
fwrite(Format, Args) ->
build(scan(Format, Args)).
-spec fwrite(Format, Data, Options) -> io_lib:chars() when
Format :: io:format(),
Data :: [term()],
Options :: [Option],
Option :: {'chars_limit', CharsLimit},
CharsLimit :: io_lib:chars_limit().
-spec fwrite(Format :: io:format(), Data :: [term()], Options :: [{'chars_limit', CharsLimit :: integer()}]) -> eFmt:chars().
fwrite(Format, Args, Options) ->
build(scan(Format, Args), Options).
%% Build the output text for a pre-parsed format list.
-spec build(FormatList) -> io_lib:chars() when
FormatList :: [char() | io_lib:format_spec()].
-spec build(FormatList :: [char() | eFmt:format_spec()]) -> eFmt:chars().
build(Cs) ->
build(Cs, []).
-spec build(FormatList, Options) -> io_lib:chars() when
FormatList :: [char() | io_lib:format_spec()],
Options :: [Option],
Option :: {'chars_limit', CharsLimit},
CharsLimit :: io_lib:chars_limit().
-spec build(FormatList :: [char() | eFmt:format_spec()], Options :: [{'chars_limit', CharsLimit :: integer()}]) -> eFmt:chars().
build(Cs, Options) ->
CharsLimit = get_option(chars_limit, Options, -1),
CharsLimit = getOpt(chars_limit, Options, -1),
Res1 = build_small(Cs),
{P, S, W, Other} = count_small(Res1),
{P, S, W, Other} = count_small(Res1, 0, 0, 0, 0),
case P + S + W of
0 ->
Res1;
@ -74,62 +58,17 @@ build(Cs, Options) ->
-spec scan(Format, Data) -> FormatList when
Format :: io:format(),
Data :: [term()],
FormatList :: [char() | io_lib:format_spec()].
FormatList :: [char() | eFmt:format_spec()].
scan(Format, Args) when is_atom(Format) ->
scan(atom_to_list(Format), Args);
scan(Format, Args) when is_binary(Format) ->
scan(binary_to_list(Format), Args);
scan(Format, Args) ->
collect(Format, Args).
%% Revert a pre-parsed format list to a plain character list and a
%% list of arguments.
-spec unscan(FormatList) -> {Format, Data} when
FormatList :: [char() | io_lib:format_spec()],
Format :: io:format(),
Data :: [term()].
unscan(Cs) ->
{print(Cs), args(Cs)}.
args([#{args := As} | Cs]) ->
As ++ args(Cs);
args([_C | Cs]) ->
args(Cs);
args([]) ->
[].
print([#{control_char := C, width := F, adjust := Ad, precision := P,
pad_char := Pad, encoding := Encoding, strings := Strings} | Cs]) ->
print(C, F, Ad, P, Pad, Encoding, Strings) ++ print(Cs);
print([C | Cs]) ->
[C | print(Cs)];
print([]) ->
[].
print(C, F, Ad, P, Pad, Encoding, Strings) ->
[$~] ++ print_field_width(F, Ad) ++ print_precision(P, Pad) ++
print_pad_char(Pad) ++ print_encoding(Encoding) ++
print_strings(Strings) ++ [C].
print_field_width(none, _Ad) -> "";
print_field_width(F, left) -> integer_to_list(-F);
print_field_width(F, right) -> integer_to_list(F).
print_precision(none, $\s) -> "";
print_precision(none, _Pad) -> "."; % pad must be second dot
print_precision(P, _Pad) -> [$. | integer_to_list(P)].
print_pad_char($\s) -> ""; % default, no need to make explicit
print_pad_char(Pad) -> [$., Pad].
print_encoding(unicode) -> "t";
print_encoding(latin1) -> "".
print_strings(false) -> "l";
print_strings(true) -> "".
if
is_atom(Format) ->
collect(atom_to_binary(Format, utf8), Args);
is_list(Format) ->
collect(list_to_binary(Format), Args);
true ->
collect(Format, Args)
end.
collect([$~ | Fmt0], Args0) ->
{C, Fmt1, Args1} = collect_cseq(Fmt0, Args0),
@ -217,32 +156,25 @@ collect_cc([$~ | Fmt], Args) when is_list(Args) -> {$~, [], Fmt, Args};
collect_cc([$n | Fmt], Args) when is_list(Args) -> {$n, [], Fmt, Args};
collect_cc([$i | Fmt], [A | Args]) -> {$i, [A], Fmt, Args}.
%% count_small([ControlC]) -> Count.
%% Count the number of big (pPwWsS) print requests and
%% number of characters of other print (small) requests.
count_small(Cs) ->
count_small(Cs, #{p => 0, s => 0, w => 0, other => 0}).
count_small([#{control_char := $p} | Cs], #{p := P} = Cnts) ->
count_small(Cs, Cnts#{p := P + 1});
count_small([#{control_char := $P} | Cs], #{p := P} = Cnts) ->
count_small(Cs, Cnts#{p := P + 1});
count_small([#{control_char := $w} | Cs], #{w := W} = Cnts) ->
count_small(Cs, Cnts#{w := W + 1});
count_small([#{control_char := $W} | Cs], #{w := W} = Cnts) ->
count_small(Cs, Cnts#{w := W + 1});
count_small([#{control_char := $s} | Cs], #{w := W} = Cnts) ->
count_small(Cs, Cnts#{w := W + 1});
count_small([S | Cs], #{other := Other} = Cnts) when is_list(S);
is_binary(S) ->
count_small(Cs, Cnts#{other := Other + io_lib:chars_length(S)});
count_small([C | Cs], #{other := Other} = Cnts) when is_integer(C) ->
count_small(Cs, Cnts#{other := Other + 1});
count_small([], #{p := P, s := S, w := W, other := Other}) ->
%% count_small[ControlC]->pPwWsS
count_small([#{control_char := $p} | Cs], P, S, W, Other) ->
count_small(Cs, P + 1, S, W, Other);
count_small([#{control_char := $P} | Cs], P, S, W, Other) ->
count_small(Cs, P + 1, S, W, Other);
count_small([#{control_char := $w} | Cs], P, S, W, Other) ->
count_small(Cs, P, S, W + 1, Other);
count_small([#{control_char := $W} | Cs], P, S, W, Other) ->
count_small(Cs, P, S, W + 1, Other);
count_small([#{control_char := $s} | Cs], P, S, W, Other) ->
count_small(Cs, P, S, W + 1, Other);
count_small([S | Cs], P, S, W, Other) when is_list(S);is_binary(S) ->
count_small(Cs, P, S, W, Other + eFmt:chars_length(S));
count_small([C | Cs], P, S, W, Other) when is_integer(C) ->
count_small(Cs, P, S, W, Other + 1);
count_small([], P, S, W, Other) ->
{P, S, W, Other}.
%% build_small([Control]) -> io_lib:chars().
%% build_small([Control]) -> eFmt:chars().
%% Interpret the control structures, but only the small ones.
%% The big ones are saved for later.
%% build_limited([Control], NumberOfPps, NumberOfLimited,
@ -251,8 +183,7 @@ count_small([], #{p := P, s := S, w := W, other := Other}) ->
%% remaining and only calculate indentation when necessary. Must also
%% be smart when calculating indentation for characters in format.
build_small([#{control_char := C, args := As, width := F, adjust := Ad,
precision := P, pad_char := Pad, encoding := Enc} = CC | Cs]) ->
build_small([#{control_char := C, args := As, width := F, adjust := Ad, precision := P, pad_char := Pad, encoding := Enc} = CC | Cs]) ->
case control_small(C, As, F, Ad, P, Pad, Enc) of
not_small -> [CC | build_small(Cs)];
S -> lists:flatten(S) ++ build_small(Cs)
@ -274,7 +205,7 @@ build_limited([#{control_char := C, args := As, width := F, adjust := Ad,
MaxLen0 < 0 -> % optimization
MaxLen0;
true ->
Len = io_lib:chars_length(S),
Len = eFmt:chars_length(S),
sub(MaxLen0, Len)
end,
if
@ -298,7 +229,7 @@ decr_pc(_, Pc) -> Pc.
%% indentation. We assume tabs at 8 cols.
-spec indentation(String, StartIndent) -> integer() when
String :: io_lib:chars(),
String :: eFmt:chars(),
StartIndent :: integer().
indentation([$\n | Cs], _I) -> indentation(Cs, 0);
@ -334,13 +265,13 @@ control_small($x, [A, Prefix], F, Adj, P, Pad, _Enc) when is_integer(A),
is_atom(Prefix) ->
prefixed_integer(A, F, Adj, base(P), Pad, atom_to_list(Prefix), true);
control_small($x, [A, Prefix], F, Adj, P, Pad, _Enc) when is_integer(A) ->
true = io_lib:deep_char_list(Prefix), %Check if Prefix a character list
true = eFmt:deep_char_list(Prefix), %Check if Prefix a character list
prefixed_integer(A, F, Adj, base(P), Pad, Prefix, true);
control_small($X, [A, Prefix], F, Adj, P, Pad, _Enc) when is_integer(A),
is_atom(Prefix) ->
prefixed_integer(A, F, Adj, base(P), Pad, atom_to_list(Prefix), false);
control_small($X, [A, Prefix], F, Adj, P, Pad, _Enc) when is_integer(A) ->
true = io_lib:deep_char_list(Prefix), %Check if Prefix a character list
true = eFmt:deep_char_list(Prefix), %Check if Prefix a character list
prefixed_integer(A, F, Adj, base(P), Pad, Prefix, false);
control_small($+, [A], F, Adj, P, Pad, _Enc) when is_integer(A) ->
Base = base(P),
@ -366,13 +297,13 @@ control_limited($s, [L0], F, Adj, P, Pad, unicode = Enc, _Str, CL, _I) ->
L = cdata_to_chars(L0, F, CL),
uniconv(string(L, limit_field(F, CL), Adj, P, Pad, Enc));
control_limited($w, [A], F, Adj, P, Pad, Enc, _Str, CL, _I) ->
Chars = io_lib:write(A, [{depth, -1}, {encoding, Enc}, {chars_limit, CL}]),
Chars = eFmt:write(A, [{depth, -1}, {encoding, Enc}, {chars_limit, CL}]),
term(Chars, F, Adj, P, Pad);
control_limited($p, [A], F, Adj, P, Pad, Enc, Str, CL, I) ->
print(A, -1, F, Adj, P, Pad, Enc, Str, CL, I);
control_limited($W, [A, Depth], F, Adj, P, Pad, Enc, _Str, CL, _I)
when is_integer(Depth) ->
Chars = io_lib:write(A, [{depth, Depth}, {encoding, Enc}, {chars_limit, CL}]),
Chars = eFmt:write(A, [{depth, Depth}, {encoding, Enc}, {chars_limit, CL}]),
term(Chars, F, Adj, P, Pad);
control_limited($P, [A, Depth], F, Adj, P, Pad, Enc, Str, CL, I)
when is_integer(Depth) ->
@ -399,7 +330,7 @@ base(B) when is_integer(B) ->
term(T, none, _Adj, none, _Pad) -> T;
term(T, none, Adj, P, Pad) -> term(T, P, Adj, P, Pad);
term(T, F, Adj, P0, Pad) ->
L = io_lib:chars_length(T),
L = eFmt:chars_length(T),
P = erlang:min(L, case P0 of none -> F; _ -> min(P0, F) end),
if
L > P ->
@ -424,7 +355,7 @@ print(T, D, F, right, P, _Pad, Enc, Str, ChLim, _I) ->
{depth, D},
{encoding, Enc},
{strings, Str}],
io_lib_pretty:print(T, Options).
eFmt_pretty:print(T, Options).
%% fwrite_e(Float, Field, Adjust, Precision, PadChar)
@ -791,11 +722,11 @@ limit_field(F, CharsLimit) ->
string(S, none, _Adj, none, _Pad, _Enc) -> S;
string(S, F, Adj, none, Pad, Enc) ->
string_field(S, F, Adj, io_lib:chars_length(S), Pad, Enc);
string_field(S, F, Adj, eFmt:chars_length(S), Pad, Enc);
string(S, none, _Adj, P, Pad, Enc) ->
string_field(S, P, left, io_lib:chars_length(S), Pad, Enc);
string_field(S, P, left, eFmt:chars_length(S), Pad, Enc);
string(S, F, Adj, P, Pad, Enc) when F >= P ->
N = io_lib:chars_length(S),
N = eFmt:chars_length(S),
if F > P ->
if N > P ->
adjust(flat_trunc(S, P, Enc), chars(Pad, F - P), Adj);
@ -909,9 +840,56 @@ sub(T, _) when T < 0 -> T;
sub(T, E) when T >= E -> T - E;
sub(_, _) -> 0.
get_option(Key, TupleList, Default) ->
getOpt(Key, TupleList, Default) ->
case lists:keyfind(Key, 1, TupleList) of
false -> Default;
{Key, Value} -> Value;
_ -> Default
{_, Value} ->
Value;
_ ->
Default
end.
%%
-spec unscan(FormatList) -> {Format, Data} when
FormatList :: [char() | eFmt:format_spec()],
Format :: io:format(),
Data :: [term()].
unscan(Cs) ->
{print(Cs), args(Cs)}.
args([#{args := As} | Cs]) ->
As ++ args(Cs);
args([_C | Cs]) ->
args(Cs);
args([]) ->
[].
print([#{control_char := C, width := F, adjust := Ad, precision := P,
pad_char := Pad, encoding := Encoding, strings := Strings} | Cs]) ->
print(C, F, Ad, P, Pad, Encoding, Strings) ++ print(Cs);
print([C | Cs]) ->
[C | print(Cs)];
print([]) ->
[].
print(C, F, Ad, P, Pad, Encoding, Strings) ->
[$~] ++ print_field_width(F, Ad) ++ print_precision(P, Pad) ++
print_pad_char(Pad) ++ print_encoding(Encoding) ++
print_strings(Strings) ++ [C].
print_field_width(none, _Ad) -> "";
print_field_width(F, left) -> integer_to_list(-F);
print_field_width(F, right) -> integer_to_list(F).
print_precision(none, $\s) -> "";
print_precision(none, _Pad) -> "."; % pad must be second dot
print_precision(P, _Pad) -> [$. | integer_to_list(P)].
print_pad_char($\s) -> ""; % default, no need to make explicit
print_pad_char(Pad) -> [$., Pad].
print_encoding(unicode) -> "t";
print_encoding(latin1) -> "".
print_strings(false) -> "l";
print_strings(true) -> "".

正在加载...
取消
保存