Skip to content

Commit

Permalink
feat: 优化回调函数
Browse files Browse the repository at this point in the history
  • Loading branch information
wangfengming committed Oct 9, 2022
1 parent 603534b commit 836e43a
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 40 deletions.
30 changes: 22 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

```
import com.meituan.olee.OneLineExpressionEvaluator;
import com.meituan.olee.evaluator.Expression;
OneLineExpressionEvaluator evaluator = new OneLineExpressionEvaluator();
Expand All @@ -16,7 +17,7 @@ expression.evaluate('1+x', new HashMap<String, Number>() {{
}});
// => 3
Expression<?> exp = evaluator.compile('1+x');
Expression exp = evaluator.compile('1+x');
exp.evalute(new HashMap<String, Number>() {{
put("x", 2);
}});
Expand Down Expand Up @@ -57,6 +58,9 @@ TODO
若需要支持其他类型实例,需要进行设置,如:

```
import com.meituan.olee.evaluator.DefaultPropertyAccessor;
import com.meituan.olee.exceptions.EvaluateException;
class User {
private final String name;
private final int age;
Expand All @@ -77,7 +81,7 @@ class User {
OneLineExpressionEvaluator evaluator = new OneLineExpressionEvaluator(new DefaultPropertyAccessor() {
@Override
public Object get(Object target, Object key) throws EvaluateException {
public Object get(Object target, Object key, boolean computed) throws EvaluateException {
if (target instanceof User) {
if ("name".equals(key)) {
return ((User) target).getName();
Expand All @@ -86,7 +90,7 @@ OneLineExpressionEvaluator evaluator = new OneLineExpressionEvaluator(new Defaul
return ((User) target).getAge();
}
}
return super.get(target, key);
return super.get(target, key, computed);
}
});
User variables1 = new User("张三", 18);
Expand Down Expand Up @@ -156,6 +160,9 @@ assertEquals(18, (int) evaluator.evaluate("user.age", variables2));
- 新增二元操作符

```
import com.meituan.olee.grammar.BinaryOpGrammar;
import com.meituan.olee.exceptions.EvaluateException;
evaluator.addBinaryOp("_=", new BinaryOpGrammar(20) {
@Override
public Object apply(Object left, Object right) throws EvaluateException {
Expand All @@ -174,6 +181,9 @@ evaluator.evaluate("'FOO' _= 'foo'", null);
- 新增一元操作符

```
import com.meituan.olee.grammar.UnaryOpGrammar;
import com.meituan.olee.exceptions.EvaluateException;
evaluator.addUnaryOp("~", new UnaryOpGrammar(1000) {
@Override
public Object apply(Object right) throws EvaluateException {
Expand Down Expand Up @@ -213,9 +223,11 @@ evaluator.evaluate("1+2", null); // => throws
支持方法调用,如:

```
import com.meituan.olee.grammar.Callback;
Map<String, Object> variables = new HashMap<String, Object>() {{
put("foo", 10);
put("double", (Function<List<?>, Number>) ((args) -> ((Number) args.get(0)).longValue() * 2));
put("double", (Callback) ((args) -> ((Number) args[0]).longValue() * 2));
}};
evaluator.evaluate("double(foo)+3", variables);
Expand Down Expand Up @@ -246,17 +258,19 @@ evaluator.evaluate("double(foo)+3", variables);
示例:

```
import com.meituan.olee.grammar.Callback;
OneLineExpressionEvaluator evaluator = new OneLineExpressionEvaluator();
evaluator.addTransform(
"filter",
(args) -> ((List<?>) args.get(0)).stream()
.filter((item) -> ((Function<List<?>, Boolean>) args.get(1)).apply(Collections.singletonList(item)))
(args) -> ((List<?>) args[0]).stream()
.filter((item) -> (Boolean) ((Callback) args[1]).apply(item))
.collect(Collectors.toList())
);
evaluator.addTransform(
"map",
(args) -> ((List<?>) args.get(0)).stream()
.map((item) -> ((Function<List<?>, Object>) args.get(1)).apply(Collections.singletonList(item)))
(args) -> ((List<?>) args[0]).stream()
.map((item) -> ((Callback) args[1]).apply(item))
.collect(Collectors.toList())
);
Map<String, Object> variables = new HashMap<String, Object>() {{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.meituan.olee.evaluator.Expression;
import com.meituan.olee.evaluator.PropertyAccessor;
import com.meituan.olee.grammar.BinaryOpGrammar;
import com.meituan.olee.grammar.Callback;
import com.meituan.olee.grammar.Grammar;
import com.meituan.olee.grammar.UnaryOpGrammar;
import com.meituan.olee.parser.Parser;
Expand All @@ -14,7 +15,6 @@
import com.meituan.olee.tokenizer.Tokenizer;

import java.util.List;
import java.util.function.Function;

public class OneLineExpressionEvaluator {
private final Grammar grammar;
Expand Down Expand Up @@ -61,7 +61,7 @@ public void addUnaryOp(String operator, UnaryOpGrammar unaryOp) {
this.tokenizer.updateGrammar(this.grammar);
}

public void addTransform(String name, Function<List<?>, ?> fn) {
public void addTransform(String name, Callback fn) {
this.grammar.transforms.put(name, fn);
}

Expand Down
10 changes: 5 additions & 5 deletions src/main/java/com/meituan/olee/ast/FunctionCallNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

import com.meituan.olee.evaluator.EvaluateContext;
import com.meituan.olee.exceptions.EvaluateException;
import com.meituan.olee.grammar.Callback;
import com.meituan.olee.grammar.Grammar;

import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

public class FunctionCallNode extends AstNode {
Expand Down Expand Up @@ -55,13 +55,13 @@ public Object evaluate(EvaluateContext context) {
throw new EvaluateException("null is not a function");
}
context.leftNull = false;
if (!(fn instanceof Function)) {
if (!(fn instanceof Callback)) {
throw new EvaluateException(fn + " is not a function");
}
List<Object> args = this.args.stream()
Object[] args = this.args.stream()
.map((node) -> node.evaluate(context))
.collect(Collectors.toList());
return ((Function<List<Object>, Object>) fn).apply(args);
.toArray();
return ((Callback) fn).apply(args);
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/meituan/olee/ast/IdentifierNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public IdentifierNode argIndex(int argIndex) {
@Override
public Object evaluate(EvaluateContext context) throws EvaluateException {
if (context.args != null && this.isArg) {
return context.args.get(this.argIndex);
return context.args[this.argIndex];
}
if (context.locals != null && context.locals.containsKey(this.value)) {
return context.locals.get(this.value);
Expand Down
5 changes: 2 additions & 3 deletions src/main/java/com/meituan/olee/ast/LambdaNode.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package com.meituan.olee.ast;

import com.meituan.olee.evaluator.EvaluateContext;
import com.meituan.olee.grammar.Callback;
import com.meituan.olee.grammar.Grammar;

import java.util.List;
import java.util.Objects;
import java.util.function.Function;

public class LambdaNode extends AstNode {
public AstNode expr;
Expand All @@ -15,7 +14,7 @@ public LambdaNode(AstNode expr) {
}

@Override
public Function<List<Object>, Object> evaluate(EvaluateContext context) {
public Callback evaluate(EvaluateContext context) {
return (args) -> this.expr.evaluate(context.withArgs(args));
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/meituan/olee/evaluator/EvaluateContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class EvaluateContext {
public Grammar grammar;
public Object variables;
public Map<String, Object> locals;
public List<Object> args;
public Object[] args;
public boolean leftNull;

public EvaluateContext(PropertyAccessor propertyAccessor, Grammar grammar, Object variables) {
Expand Down Expand Up @@ -41,7 +41,7 @@ public void addLocal(String name, Object variable) {
this.locals.put(name, variable);
}

public EvaluateContext withArgs(List<Object> args) {
public EvaluateContext withArgs(Object[] args) {
EvaluateContext newContext = this.copy();
newContext.args = args;
return newContext;
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/com/meituan/olee/grammar/Callback.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.meituan.olee.grammar;

@FunctionalInterface
public interface Callback {
Object apply(Object... args);
}
4 changes: 1 addition & 3 deletions src/main/java/com/meituan/olee/grammar/Grammar.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
import com.meituan.olee.grammar.unaryOps.UnaryPlus;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
* priority:
Expand All @@ -34,7 +32,7 @@ public class Grammar {
public final Map<String, SymbolGrammar> symbols;
public final Map<String, UnaryOpGrammar> unaryOps;
public final Map<String, BinaryOpGrammar> binaryOps;
public final Map<String, Function<List<?>, ?>> transforms = new HashMap<>();
public final Map<String, Callback> transforms = new HashMap<>();

public Grammar() {
this.symbols = this.initSymbols();
Expand Down
49 changes: 33 additions & 16 deletions src/test/java/com/meituan/olee/EvaluateTest.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.meituan.olee;

import com.meituan.olee.exceptions.EvaluateException;
import com.meituan.olee.grammar.Callback;
import com.meituan.olee.util.OperatorUtils;
import org.junit.jupiter.api.Test;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.junit.jupiter.api.Assertions.*;
Expand Down Expand Up @@ -161,7 +162,7 @@ void member() {
void optional() {
Map<String, Object> variables = new HashMap<>();
variables.put("val", Collections.emptyMap());
variables.put("fun", (Function<List<?>, ?>) (args) -> null);
variables.put("fun", (Callback) (args) -> null);

assertNull(this.evaluator.evaluate("foo?.bar.baz", variables));
Exception exception1 = assertThrows(EvaluateException.class, () -> {
Expand Down Expand Up @@ -273,18 +274,18 @@ void transform() {
OneLineExpressionEvaluator evaluator = new OneLineExpressionEvaluator();
evaluator.addTransform(
"half",
(args) -> ((Number) args.get(0)).doubleValue() / 2
(args) -> ((Number) args[0]).doubleValue() / 2
);
evaluator.addTransform(
"filter",
(args) -> ((List<?>) args.get(0)).stream()
.filter((item) -> ((Function<List<?>, Boolean>) args.get(1)).apply(Collections.singletonList(item)))
(args) -> ((List<?>) args[0]).stream()
.filter((item) -> !OperatorUtils.isFalsy(((Callback) args[1]).apply(item)))
.collect(Collectors.toList())
);
evaluator.addTransform(
"map",
(args) -> ((List<?>) args.get(0)).stream()
.map((item) -> ((Function<List<?>, Object>) args.get(1)).apply(Collections.singletonList(item)))
(args) -> ((List<?>) args[0]).stream()
.map((item) -> ((Callback) args[1]).apply(item))
.collect(Collectors.toList())
);
Map<String, Object> variables = new HashMap<String, Object>() {{
Expand All @@ -295,9 +296,9 @@ void transform() {
add(Collections.singletonMap("tek", "baz"));
add(Collections.singletonMap("tok", "baz"));
}});
put("double", (Function<List<?>, Number>) ((args) -> ((Number) args.get(0)).longValue() * 2));
put("double", (Callback) ((args) -> ((Number) args[0]).longValue() * 2));
put("fns", Collections.singletonMap(
"half", (Function<List<?>, Number>) ((args) -> ((Number) args.get(0)).doubleValue() / 2)
"half", (Callback) ((args) -> ((Number) args[0]).doubleValue() / 2)
));
}};

Expand All @@ -322,6 +323,14 @@ void transform() {
}},
evaluator.evaluate("bar|map('1'+(@.tek||@.tok))", variables)
);
assertEquals(
new ArrayList<Object>() {{
add("hello");
add("baz");
add("baz");
}},
evaluator.evaluate("bar|filter(@.tek)|map(@.tek)", variables)
);

Exception exception1 = assertThrows(EvaluateException.class, () -> {
this.evaluator.evaluate("'hello'|world", variables);
Expand All @@ -334,18 +343,18 @@ void functionCall() {
OneLineExpressionEvaluator evaluator = new OneLineExpressionEvaluator();
evaluator.addTransform(
"half",
(args) -> ((Number) args.get(0)).doubleValue() / 2
(args) -> ((Number) args[0]).doubleValue() / 2
);
evaluator.addTransform(
"filter",
(args) -> ((List<?>) args.get(0)).stream()
.filter((item) -> ((Function<List<?>, Boolean>) args.get(1)).apply(Collections.singletonList(item)))
(args) -> ((List<?>) args[0]).stream()
.filter((item) -> !OperatorUtils.isFalsy(((Callback) args[1]).apply(item)))
.collect(Collectors.toList())
);
evaluator.addTransform(
"map",
(args) -> ((List<?>) args.get(0)).stream()
.map((item) -> ((Function<List<?>, Object>) args.get(1)).apply(Collections.singletonList(item)))
(args) -> ((List<?>) args[0]).stream()
.map((item) -> ((Callback) args[1]).apply(item))
.collect(Collectors.toList())
);
Map<String, Object> variables = new HashMap<String, Object>() {{
Expand All @@ -356,9 +365,9 @@ void functionCall() {
add(Collections.singletonMap("tek", "baz"));
add(Collections.singletonMap("tok", "baz"));
}});
put("double", (Function<List<?>, Number>) ((args) -> ((Number) args.get(0)).longValue() * 2));
put("double", (Callback) ((args) -> ((Number) args[0]).longValue() * 2));
put("fns", Collections.singletonMap(
"half", (Function<List<?>, Number>) ((args) -> ((Number) args.get(0)).doubleValue() / 2)
"half", (Callback) ((args) -> ((Number) args[0]).doubleValue() / 2)
));
}};

Expand All @@ -383,6 +392,14 @@ void functionCall() {
}},
evaluator.evaluate("map(bar,'1'+(@.tek||@.tok))", variables)
);
assertEquals(
new ArrayList<Object>() {{
add("hello");
add("baz");
add("baz");
}},
evaluator.evaluate("map(filter(bar,@.tek),@.tek)", variables)
);

Exception exception1 = assertThrows(EvaluateException.class, () -> {
this.evaluator.evaluate("world('hello')", variables);
Expand Down

0 comments on commit 836e43a

Please sign in to comment.