Skip to content

MtFmT-Lib/mtfmt

Repository files navigation

MtFmT

AUTHOR LICENSE LANGUAGE Coverage Status

TESTS DYLIB-BUILD LIB-BUILD CMAKE-BUILD CLANG-FORMAT

| HomePage | Document | Code Document | Pre-build Libs | Pre-build DLLs |

mtfmt (Mini template formatter)是为嵌入式系统设计的格式化库,它实现了 PEP-3101 中的格式化串的一个方言。其它类似的实现包括 {fmtlib} 等。另外,mtfmt 还实现了.Net中对于日期和时间格式化中的标准部分用户定义部分的一个子集,mtfmt的其主要特性包括:

  • 无除法运算、取模运算的有符号/无符号整数格式化(二进制、八进制、十进制、十六进制)
  • 无除法运算、取模运算的有符号/无符号的量化值格式化(q31_t等)
  • 无除法运算、取模运算的有符号/无符号定点数格式化
  • 标准日期时间格式化、指定分隔符、项位置的日期和时间格式化
  • 12小时制
  • 星期名称
  • 字符串格式化
  • 指定对齐方式、填充宽度、符号显示方式
  • C数组格式化
  • 不依赖操作系统,提供用于嵌入式设备的内存分配器
  • 提供动态长度字符串,字符串携带 stack allocator
  • 提供可传入 va_list 函数,变参函数
  • 定位parser错误位置以及原因

请注意区分于大多数的格式化器,MtFmt-Core 暂不提供如下的功能:

  • 自定义格式化标记
  • 自定义错误处理

例子

整数格式化(二进制、八进制、十进制、十六进制)

mstr_print("{0:i32:b}\n", 165); // => 10100101
mstr_print("{0:i32:o}\n", 165); // => 245
mstr_print("{0:i32:d}\n", 165); // => 165
mstr_print("{0:i32:h}\n", 165); // => a5
mstr_print("{0:i32:H}\n", 165); // => A5
mstr_print("{0:i32:x}\n", 165); // => 0xa5
mstr_print("{0:i32:X}\n", 165); // => 0XA5

对齐方式(左对齐、居中、右对齐)

mstr_print("|{0:i32:=8}|\n", 1234); // => |  1234  |
mstr_print("|{0:i32:<8}|\n", 1234); // => |1234    |
mstr_print("|{0:i32:>8}|\n", 1234); // => |    1234|

数组格式化,且指定格式化采用的进制

const uint32_t array[5] = {
    0x12, 0x34, 0x56, 0x78, 0x90
};
const usize_t array_size = 5;
mstr_print("{[0:i32]}\n", array, array_size);
// => 18, 52, 86, 120, 144
mstr_print("{[0:i32:x]}\n", array, array_size);
// => 0x12, 0x34, 0x56, 0x78, 0x90

更多的例子在 examples文件夹 里面哦

工作方式

MtFmt 通过数个 pass 来完成格式化工作,其均是动态的,可以分为输入串解析(Scanner)、格式化串解析(Replacement parser)和转换(Converter)三个过程。

graph LR;
    Scanner --> Parser --> Converter;

三个 pass 是互不依赖的,mtfmt 提供了每个 pass 的函数导出:

  • Scanner : mm_fmt.h 文件中的 mstr_format 函数
  • Parser: mm_parser.h
  • Converter: mm_fmt.h 文件中的 mstr_fmt_XXX 命名方式的函数

语法描述

这里给出了 mtfmt 的格式化语法细节。其使用一个小可爱语言来完成它,姑且称作 MtFmt-DSL 。MtFmt 通过 replacement_field 区分需要放置的值类型,使用 {} 包起来。对于 replacement field,其至少需要指定参数位置以及参数类型,且参数位置必须是递增的。若使用 { A } 表示0到多个的A, [ A ] 表示可选的A,{ A }+ 表示1到多个的A,{ A }? 表示0或1个A。使用不同的行或者“|”以表示“或”,使用缩进区分符号。下面给出了总体语法。

replacement_field :=
    `{` arg_id `:` arg_type [`:` format_spec] `}`
    `{[` arg_id `:` arg_type  [`|:` split_ch] [`:` format_spec] `]}`

split_ch :=
    split_ch_content
    split_ch split_ch_content

split_ch_content :=
    any characters other than `:`, `}`, or `]}`

format_spec :=
    [ [fill] align] [sign] [width] [format_type | chrono_spec]

chrono_spec :=
    standard_chrono
    user_def_chrono

arg_id: 参数位置

arg_id :=
    { digit }+

digit :=
    one of
        0 1 2 3 4 5 6 7 8 9

参数位置指定当前的 replacement field 需要填充第几个参数,需要注意的是,参数位置是递增的,例如,第一个和第二个例子是合法的arg_id,而第三个不是:

"{0:u32} + {1:u32}"
"{0:u32} + {1:u32} + {0:u32}"
"{1:u32} + {0:u32}"

参数位置可以是任意个数的整数值。

arg_type: 参数类型

arg_type :=
    one of
        i8  i16  i32
        u8  u16  u32
        q { digit }+ [ `u` ]
        F { digit }+ [ `.` { digit }+ ] [ `u` ]
        s
        c
        t

所有预置的类型如下:

预置类型 描述 示例
i8 8位有符号整数
i16 16位有符号整数
i32 32位有符号整数
u8 8位无符号整数
u16 16位无符号整数
u32 32位无符号整数
qXX 量化值 q12 表示 12 位量化值,q12u 表示12位无符号量化值
FXX.XXX 定点数 F12.4 表示 12 位整数,4位小数的定点值,F12.4u 表示相应的无符号值
s C字符串指针
t 时间和日期

fill,align: 填充与对齐

fill :=
    a character other than `]}`, `|`, or `}`

align :=
    one of
        < = >

填充与对齐是可选部分,其指定填充字符和其中内容的对齐方式。需要注意的是,其必须在 width 项被指定的时候才有效。否则由于 width 是默认值,不会造成任何效果。填充项是单个的字符,但是其不能是 ]|};目前支持的对齐项如下表:

对齐选项 描述 示例 示例结果
< 左对齐 format("{0:i32:#<8}", ..., 1) 1#######
= 居中 format("{0:i32:#=8}", ..., 1) ###1####
> 右对齐 format("{0:i32:#>8}", ..., 1) #######1

sign: 符号显示

sign :=
    one of
        +   -   ` `

符号项指定平凡值类型(包括 u8u16u32i8i16i32)、量化值 qXX、定点数 FXX.XX的符号显示方式,对于其余类型无效。目前支持的值符号显示选项如下:

符号选项 描述 示例 示例结果
+ 始终显示符号 format("{0:i32:+},{1:i32:+}", ..., 1, -1) +1,-1
- 仅在值小于0时显示符号 format("{0:i32:-},{1:i32:-}", ..., 1, -1) 1,-1
ASCII空格 在值小于0时显示符号,否则显示空格 format("%{0:i32: },{1:i32: }%", ..., 1, -1) % 1,-1%

width: 项宽度

width :=
    { digit }+

项宽度指定一个最小值,其是任意一个整数值。

format_type: 值格式化标记

format_type :=
    one of
        b   d   o   h   H   x   X

值格式化标记指定了平凡值类型的格式化方式。所有项的含义如下:

格式化标记 描述 示例 示例结果
b 二进制值 format("{0:i32:b}", ..., 10) 1010
d 十进制值(默认) format("{0:i32:d}", ..., 10) 10
o 八进制值 format("{0:i32:o}", ..., 10) 12
h 十六进制值 format("{0:i32:h}", ..., 10) a
H 十六进制值(大写) format("{0:i32:H}", ..., 10) A
x 十六进制值(带 0x前缀) format("{0:i32:x}", ..., 10) 0xa
X 十六进制值(大写,带 0X前缀) format("{0:i32:X}", ..., 10) 0XA

standard_chrono: 标准日期时间格式化项

standard_chrono :=
    one of
        `%` f
        `%` g

标准日期格式化项提供了两个预置的格式化方式,其列表如下:

选项 描述 描述字符串 示例 示例结果
f 完整日期 %yyyy-%MM-%dd %HH:%mm:%ss.%xxxx %w format("{0:t:%f}", ...) 1919-05-04 14:01:02.1234 7
g 标准日期 %yyyy-%MM-%dd %HH:%mm:%ss.%xxxx format("{0:t:%g}", ...) 1919-05-04 14:01:02.1234

为什么星期显示不是 Sunday呢,因为 (●'◡'●) 。

user_def_chrono: 自定义日期时间格式化项

user_def_chrono :=
    `%` chrono_fromat_type
    `%` chrono_fromat_type chrono_split_string user_def_chrono

自定义日期时间格式化项包括指定如何格式化每个项目和如何指定项之间的分割两个部分。

chrono_type: 项格式化

chrono_fromat_type :=
    one of
        y   yy   yyy   yyyy   yyyyy
        M   MM
        d   dd
        h   hh
        H   HH
        m   mm
        s   ss
        x   xx   xxx   xxxx
        w

项格式化支持如下内容,其包括年月日时分秒,子秒,星期几个部分,所有支持的项如下:

描述 示例 示例结果 解释
y 1~2位的年份 format("{0:t:%y}", 1901-01-01...) 1 1901(最后1位)
yy 固定2位的年份 format("{0:t:%yy}", 1901-01-01...) 01 1901(最后2位,补0)
yyy 3~4位年份 format("{0:t:%yyy}", 224-01-01...) 224
yyyy 固定4位年份 format("{0:t:%yyy}", 224-01-01...) 0224 4位,进行补0
M 1~2位月份 format("{0:t:%yyy}", 224-01-01...) 1
MM 固定2位月份 format("{0:t:%yyy}", 224-01-01...) 01
d 1~2位天数 format("{0:t:%yyy}", 224-01-01...) 1
dd 固定2位天数 format("{0:t:%yyy}", 224-01-01...) 01
h 1~2位的12h制小时 format("{0:t:%yyy}", ...13:14:52:01314...) 1
hh 固定2位的12h制小时 format("{0:t:%yyy}", ...13:14:52:01314...) 01
H 1~2位的24h制小时 format("{0:t:%yyy}", ...03:14:52:01314...) 3
HH 固定2位的24h制小时 format("{0:t:%yyy}", ...03:14:52:01314...) 03
m 1~2位的分钟 format("{0:t:%yyy}", ...13:04:52:01314...) 4
mm 固定2位的分钟 format("{0:t:%yyy}", ...13:04:52:01314...) 04
s 1~2位的秒钟 format("{0:t:%yyy}", ...13:14:02:01314...) 2
ss 固定2位的秒钟 format("{0:t:%yyy}", ...13:14:02:01314...) 02
x 固定1位的亚秒值 format("{0:t:%yyy}", ...13:14:52:01314...) 0
xx 固定2位的亚秒值 format("{0:t:%yyy}", ...13:14:52:01314...) 01
xxx 固定3位的亚秒值 format("{0:t:%yyy}", ...13:14:52:01314...) 013
xxxx 固定4位的亚秒值 format("{0:t:%yyy}", ...13:14:52:01314...) 0131
w 星期值 format("{0:t:%y}", 1919-05-04...) 7

chrono_split: 项分割

chrono_split_string :=
    none, or one and more chrono_split_char

chrono_split_char :=
    any character other than `:`, `}`, or `]}`
    `%%`

项分割可以是除去 :}]} 以外的内容,且可以使用两个连续的 % 来表示 %

数组格式化

TODO

See also

TODO