1.5.0 • Published 1 year ago

djexl-js v1.5.0

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

Djel (djexl-js)

Dynamic javascript Expression Language: 一个简单的表达式解析求值器.

Djel is a fork of Jexl.

快速开始

const Djel = require('djexl-js').default;
const djel = Djel();

// add Transform
djel.addTransforms({
  upper: (val) => val.toUpperCase(),
  find: (arr, by) => arr.find(by),
});

const variables = {
  name: { first: 'Sterling', last: 'Archer' },
  assoc: [
    { first: 'Lana', last: 'Kane' },
    { first: 'Cyril', last: 'Figgis' },
    { first: 'Pam', last: 'Poovey' },
  ],
  age: 36,
};

let res;

// find in an array
res = djel.evaluate('assoc|find(@.first == "Lana").last', variables);
console.log(res); // Output: Kane

// Do math
res = djel.evaluate('age * (3 - 1)', variables);
console.log(res); // Output: 72

// Concatenate
res = djel.evaluate('name.first + " " + name["la" + "st"]', variables);
console.log(res); // Output: Sterling Archer

// Compound
res = djel.evaluate('assoc|find(@.last == "Figgis").first == "Cyril" && assoc|find(@.last == "Poovey").first == "Pam"', variables);
console.log(res); // Output: true

// Use array indexes
res = djel.evaluate('assoc[1]', variables);
console.log(res.first + ' ' + res.last); // Output: Cyril Figgis

// Use conditional logic
res = djel.evaluate('age > 62 ? "retired" : "working"', variables);
console.log(res); // Output: working

res = djel.evaluate('"duchess"|upper + " " + name.last|upper', variables);
console.log(res); // Output: DUCHESS ARCHER

// Add your own operators
// Here's a case-insensitive string equality
djel.addBinaryOps({
  '_=': {
    priority: 30,
    fn: (left, right) => left.toLowerCase() === right.toLowerCase(),
  },
});
res = djel.evaluate('"Guest" _= "gUeSt"');
console.log(res); // Output: true

安装使用

yarn add djexl-js
import Djel from 'djexl-js';

注入变量

求值时,可以注入变量。如

const variables = { age: 10 };

djel.evaluate('age * (3 - 1)', variables); // => 20

// 或者
const expression = djel.compile('age * (3 - 1)');
expression.evaluate(variables); // => 20

一元操作符

操作符符号
取反!
加号+
减号-

二元操作符

操作符符号
加号,字符串/数组/对象拼接+
-
*
/
整除//
取模%
指数^
逻辑与&&
逻辑或||
空值合并??
  • + 支持数组/对象拼接
表达式结果
[1,2]+[3,4][1,2,3,4]
{x:1}+{y:2}{x:1,y:2}

比较

操作符符号
相等==
不等!=
大于>
大于等于>=
小于<
小于等于<=
inin
  • 关于 in:

in 操作符可以检查字字符串,如:"Cad" in "Ron Cadillac"

也可以用于检查元素是否在数组中,如:"coarse" in ['fine', 'medium', 'coarse'],但是这个判断是使用引用比较,因此 {a: 'b'} in [{a: 'b'}] 的结果是 false

三元表达式

表达式结果
"" ? "Full" : "Empty""Empty"
"foo" in "foobar" ? "Yes" : "No""Yes"
{agent: "Archer"}.agent ?: "Kane""Archer"

类型

类型示例
Booleantrue, false
String"Hello \"user\"", 'Hey there!'
Number6, -7.2, 5, -3.14159
Object{hello: "world!"}
Array['hello', 'world!']

展开语法

可以在构造数组时,将数组表达式或者 string 在语法层面展开;可以在构造对象时,将对象表达式按 key-value 的方式展开。

表达式结果
[1,...[2,3],4][1,2,3,4]
[1,'23',4][1,'2','3',4]
{a:1,...{b:2,c:3},d:4}{a:1,b:2,c:3,d:4}

分组

小括号 () 按照你预期的方式使用即可。

表达式结果
(83 + 1) / 242
1 < 3 && (4 > 2 || 2 > 4)true

标识符

使用变量名访问变量,使用 .[] 访问对象属性值。

可选链运算符(?. ?.[] ?.())在引用为空 (null 或者 undefined) 的情况下不会引起错误,该表达式短路返回 undefined

示例变量 :

const vairables = {
  name: {
    first: "Malory",
    last: "Archer"
  },
  exes: [
    "Nikolai Jakov",
    "Len Trexler",
    "Burt Reynolds"
  ],
  lastEx: 2
}
表达式结果
name.first"Malory"
name["first"]"Malory"
name['la' + 'st']"Archer"
exes[2]"Burt Reynolds"
exes[lastEx - 1]"Len Trexler"
exes[-1]"Burt Reynolds"
exes[-2]"Len Trexler"
foo?.bar.bazundefined
  • 关于 [-1]:

你可以使用负数在数组或字符串尾部获取元组或字符。如 a[-1] 表示最后一个元素或者字符。

定义变量

你可以使用 def 关键词定义变量,改变量有一个局部作用域,它也可以覆盖通过 variables 设置的变量。

表达式结果
def a = 1; def b = a + 1; a + b3
def a = 1; (true ? (def a = 10; a) : 0) + a11
使用变量: const variables = { a: 1 } (true ? (def a = 10; a) : 0) + a11

函数调用

你可以像在 js 中一样调用函数,但是该函数必须定义在 variables 中。 比如变量如下:

const variables = { foo: 10, fns: { half: (v) => v / 2 } };
表达式结果
fns.half(foo) + 38
fns["half"](foo) + 38

管道

管道是函数调用的语法糖。 形如 fn(a) 的函数调用可以简写成 a|fn 或者 a|fn() 的方式; 形如 fn(a,b,c) 的函数调用可以简写成 a|fn(b,c) 的方式。 这对多次函数调用非常有帮助,如 fn3(fn2(fn1(v))) 可以写成 v|fn1|fn2|fn3

但是需要注意,v|a.b.c 等价于 a(v).b.c 而不是 a.b.c(v)a.b.c(v) 的管道形式应该是 v|(a.b.c)

特殊函数注入方式

除了使用 variables 的方式注入函数外,还可以使用 djel.addTransforms 注入函数,如:

djel.addTransforms({
  split: (var, char) => val.split(char),
  lower: (val) => val.toLowerCase(),
});
表达式结果
"Pam Poovey"|lower|split(' ')1"poovey"
"password==guest"|split('==')['password', 'guest']
split("password==guest", '==')['password', 'guest']

函数定义

定义函数的形式是 fn () => expression 或者 fn (a, b, c) => expression

示例:

const variables = {
  users: [
    { age: 18, name: "Nikolai Jakov" },
    { age: 17, name: "Len Trexler" },
    { age: 19, name: "Burt Reynolds" },
  ],
}
djel.addTransforms({
  filter: (arr, by) => arr.filter(by),
  map: (arr, by) => arr.map(by),
  sum: (arr, by) => arr.reduce((s, i) => s + (by(i) || 0), 0),
});
表达式结果
users|filter(fn(a)=>a.age<18)[{ age: 17, name: "Len Trexler" }]
users|map(fn(a)=>a.age)[18,17,19]
users|sum(fn(a)=>a.age)/users.length18
filter(users,fn(a)=>a.age<18)[{ age: 17, name: "Len Trexler" }]
map(users,fn(a)=>a.age)[18,17,19]
sum(users,fn(a)=>a.age)/users.length18

简版函数定义

可以使用 @ @0 @1 ~ @9 的特殊标识符来定义一个简版函数。 @ @0 表示第 0 个函数参数,@1 ~ @9 分别表示 第 1 ~ 9 个函数参数。比如:@.x + @1 表示 fn (a, b) => a.x + b

示例(变量和注入函数同"函数定义"的示例):

表达式结果
users|filter(@.age<18)[{ age: 17, name: "Len Trexler" }]
users|map(@.age)[18,17,19]
users|sum(@.age)/users.length18
filter(users,@.age<18)[{ age: 17, name: "Len Trexler" }]
map(users,@.age)[18,17,19]
sum(users,@.age)/users.length18

API

Djel

使用 Djel 可以创建一个实例,在这个实例你可以单独注入函数,定义、删除操作符等。

const djel = Djel()

evaluate

evaluate: (exp: string, variables?: any) => any

计算一个表达式,variables 是可选的。

compile

compile: (exp: string) => {
  evaluate: (context?: any) => any;
}

你可以先编译一个表达式,之后使用编译结果在不同变量上进行求值。如:

const expression = djel.compile('a+b');

expression.evaluate({ a: 1, b: 2 }); // => 3
expression.evaluate({ a: 3, b: 4 }); // => 7

addBinaryOps

addBinaryOps: (binaryOps: Record<string, {
  priority: number;
  fn: (left: any, right: any) => any;
}>) => void

Djel 实例中添加二元操作符。二元操作符需要考虑其左值和右值,如 "+""=="priority 属性决定了该操作符的优先级。

内置操作符的优先级如下表(见源码 src/grammar.ts):

优先级符合操作符
10|| ??逻辑或,空值合并
20&&逻辑与
30== !=相等
40<= < >= > in比较
50+ -加、拼接、减
60* / // %乘、除、整除、取余
70^指数
80|管道
90! + -一元操作符
100[] . () ?.[] ?. ?.()属性访问,函数调用

addUnaryOps

addUnaryOps: (unaryOps: Record<string, {
  priority: number;
  fn: (left: any) => any;
}>) => void

Djel 实例中添加一元操作符。一元操作符只需要考虑其右值,如 "!"priority 属性决定了该操作符的优先级。

addTransforms

addTransforms: (transforms: Record<string, Function>) => void

Djel 实例中注入函数。

removeOp

removeOp: (operator: string) => void

Djel 实例中移除操作符。如 djel.removeOp('^') 可以移除指数操作符。

removeTransform

removeTransform: (transformName: string) => void

Djel 实例中移除注入的函数。

License

Djel is licensed under the MIT license. Please see LICENSE.txt for full details.

1.5.0

1 year ago

1.4.1

2 years ago

1.4.0

2 years ago

1.3.0

2 years ago

1.2.0

2 years ago

1.1.0

2 years ago

1.0.4

2 years ago

1.0.3

2 years ago

1.0.2

2 years ago

1.0.1

2 years ago

1.0.0

2 years ago