#binary-data #binary-format #bytecode #expressions #interpreter #json

no-std bin+lib expry

在编码(二进制)值上执行表达式,产生另一个二进制值(可以是解码或编码形式)。支持自定义函数。支持解析表达式并将其转换为字节码。

7 个不稳定版本 (3 个破坏性更新)

0.4.0 2024年5月3日
0.3.0 2023年12月10日
0.2.2 2023年4月28日
0.2.1 2023年3月25日
0.1.1 2022年5月29日

#454 in 编码

Download history 218/week @ 2024-05-02 73/week @ 2024-05-09 41/week @ 2024-05-16 68/week @ 2024-05-23 97/week @ 2024-05-30 51/week @ 2024-06-06 57/week @ 2024-06-13 30/week @ 2024-06-20 14/week @ 2024-06-27 26/week @ 2024-07-04 31/week @ 2024-07-11 47/week @ 2024-07-18 13/week @ 2024-07-25 9/week @ 2024-08-01 28/week @ 2024-08-08 33/week @ 2024-08-15

每月下载量 116
5 crates 中使用

Apache-2.0

460KB
10K SLoC

expry

expry 是一个快速的无模式二进制数据格式,能够使用表达式对存储的二进制数据进行过滤和选择信息部分。表达式被编译并优化为字节码,在评估时解释这些字节码。可以使用 expry_parse_json 将 JSON 读取到这些数据结构中。由于 expry 支持二进制字符串,并非所有的 expry 值都可以导出到 JSON。支持以下数据类型:

  • null;
  • bool;
  • int64;
  • float;
  • double;
  • string;
  • array;
  • object.

表达式

二进制对象上的表达式编译为字节码,并可以通过网络传输。由于字节码在底层对二进制对象存储的方式进行优化,因此可以快速评估字节码。对象内部键以有序方式存储。如果当前表达式的评估不再需要键,则它们将被永久跳过。

  • 常量:nullfalsetrue,数字(包括:0x...0b......f),字符串,使用 'string'"string"(包括使用 r#"..."# 的原始字符串,以及带有 x'hex'x"hex" 的十六进制字符串)。
  • 条件语句:支持 if-then-else 与条件(三元)运算符:cond ? then-part : else-part
  • 带有 { 字段的对象表达式?},包含可选字段,字段之间使用 ,(最后一个条目可以有尾随的 ,)分隔。
    • 字段可以是 name: expr,其中 expr 是一个表达式或基本数据类型(见上文);
    • 字段可以是 "name": expr,其中 expr 是一个表达式或基本数据类型(见上文);
    • 字段可以是 field(或 field???),它从 this 对象中复制字段;
    • 字段可以是 (expr): expr 以动态生成字段名(必须是一个字符串),例如 { (somefield): 42, }(如果 somefield"x",则结果为 {x: 42})。
  • 数组表达式:[ 表达式?],包含可选的表达式,这些表达式由 ,(最后一个条目可以有尾随的 ,)分隔。示例:[ 42+42, 37+37, ]
  • 算术运算符:+-*/**(幂),%(取模)。
  • 位运算符:|&^(异或),<<>>
  • 逻辑运算符:&&||a ? b : c==!=>>=<<=
  • 特殊的 this 字段,用于表示当前对象。
  • 数组索引:支持 a[b](数组索引)来访问数组中的单个元素
  • 长度运算符
    • field.len(),如果字段是字符串(字节数),数组(元素数量),或对象(键值对数量),则返回该字段的长度。
  • 字符串运算符
    • a .. b:连接字符串 ab
    • a *= b:如果字符串 a 包含字符串 b,则为真。
    • a $= b:如果字符串 a 以字符串 b 结尾,则为真。
    • a ^= b:如果字符串 a 以字符串 b 开头,则为真。
  • 字段运算符
    • a.b.c.d 支持访问子字段;
    • a.get(b).get(c).get(d) 支持动态子字段;
  • 错误运算符
    • try 运算符:a ??? b:如果在评估 a(例如未定义的字段或除以零)时出现错误。简写 a ??? 等同于 a ??? null。另一种语法是 try(a, b)try(a)
    • 非空否则运算符:a ?? b:如果 a 为空,则返回 b
    • defined(x)undefined(x):检查值是否已定义(如字段查找)。
  • 字符串方法
    • .trim()
    • .lower()
    • .upper()
    • .hex()
    • .htmlescape()
    • .urlescape()
    • .sub(int[, int])(第三个参数是结果的字符串长度,默认为最大整数值)
    • .basename()
    • .dirname()
    • .splitn(max, split_on) (split_on 是分隔字符串,max 是结果数组中元素的最大数量),结果是一个字符串数组
  • 类型方法
    • .tostring()
    • .tointeger()
    • .tofloat()
    • .todouble()
  • 数组方法
    • array.extend(array) 合并两个数组。
  • 接受 lambda 函数作为参数的方法。lambda 函数指定为 |x| expr,其中 x 是 lambda 函数的参数。
    • array.filter(lambda) 过滤数组,只包含 e 元素,其中 lambda(e) 返回 true(注意 lambda 应始终返回布尔值,否则过滤将失败)
    • array.map(lambda) 将元素数组 e 映射到元素数组 lambda(e)
    • array.sort_by_key(lambda) 根据键 lambda(e) 对元素数组 e 进行排序。值的排序基于以下类型顺序:null,bool,int,float,double,string,array,object。如果类型匹配,则按内容本身进行排序。请注意,NaN 浮点数的排序将产生动态错误,因为这未定义。
    • array.to_object(lamba) 将数组转换为对象,其中键是 lambda 返回的数组中的第一个值。值是 lambda 返回的数组的第二个值。
    • array.to_map(lamba) 创建一个键 - 值数组对象。lambda 返回 [key, sort, value]。

用户定义函数

在评估过程中,还可以添加用户定义的函数。这些用户定义的函数只在评估时才知道,因此没有静态类型检查。如果参数数量或参数类型不匹配,则只生成运行时错误。

轻松将自定义数据类型转换为值

使用 From 特性,可以将自定义数据类型轻松转换为值,即使它们包含在 Vec 等数据结构中。

use expry::*;

struct Foo {
    foo: u32,
    bar: bool,
}

impl<'a> From<&'a Foo> for DecodedValue<'a> {
    fn from(v: &'a Foo) -> Self {
        value!({
            "foo": v.foo as i64,
            "bar": v.bar,
        })
    }
}

let foos = vec![
    Foo{foo:1,bar:true},
    Foo{foo:2,bar:false},
];
let encoded_value = value!({
  "foo": Foo{foo:1,bar:true},
  "foos": foos,
}).encode_to_vec(false);

无运行时依赖项