Browse Source

lambda 参数括号支持

kangjie 5 years ago
parent
commit
419f0118da

+ 16 - 0
src/main/java/org/ssssssss/expression/ExpressionEngine.java

@@ -11,6 +11,22 @@ public class ExpressionEngine {
 		return ExpressionTemplate.create(expression).render(context);
 	}
 
+	public static void main(String[] args) {
+
+		ExpressionEngine engine = new ExpressionEngine();
+
+		Map<String, Object> params = new HashMap<>();
+		params.put("abc", "876");
+		params.put("array", new String[]{"甲", "乙"});
+		ArrayList<Object> list = new ArrayList<>();
+		list.add("987654321");
+		list.add("yyy");
+		list.add("");
+		params.put("list", list);
+		Object result = engine.execute("${list.filter(e->list.filter(e->!e.isEmpty()).size() > 1)}", params);
+		System.out.println(result);
+
+	}
 
 	public Object executeWrap(String expression, Map<String, Object> variables) {
 		return execute("${" + expression + "}", variables);

+ 11 - 2
src/main/java/org/ssssssss/expression/parsing/ArrayLikeLambdaOneArgumentExecutor.java → src/main/java/org/ssssssss/expression/parsing/ArrayLikeLambdaExecutor.java

@@ -4,7 +4,7 @@ import java.lang.reflect.Method;
 import java.util.*;
 import java.util.function.Supplier;
 
-public class ArrayLikeLambdaOneArgumentExecutor {
+public class ArrayLikeLambdaExecutor {
 
     public static final Set<String> SUPPORT_METHOD;
     public static final Map<String, Method> METHODS;
@@ -15,6 +15,7 @@ public class ArrayLikeLambdaOneArgumentExecutor {
         Set<String> set = new HashSet<>();
         addSupport(temp, set, "map");
         addSupport(temp, set, "filter");
+//        addSupport(temp, set, "reduce");
         SUPPORT_METHOD = Collections.unmodifiableSet(set);
         METHODS = Collections.unmodifiableMap(temp);
     }
@@ -26,7 +27,7 @@ public class ArrayLikeLambdaOneArgumentExecutor {
 
     private static void addMethod(Map<String, Method> initialMap, String name) {
         try {
-            initialMap.put(name, ArrayLikeLambdaOneArgumentExecutor.class.getMethod(name, Object.class, Object[].class));
+            initialMap.put(name, ArrayLikeLambdaExecutor.class.getMethod(name, Object.class, Object[].class));
         } catch (NoSuchMethodException e) {
             e.printStackTrace();
             throw new RuntimeException(e);
@@ -54,6 +55,14 @@ public class ArrayLikeLambdaOneArgumentExecutor {
         throw new RuntimeException("未实现");
     }
 
+
+    @SuppressWarnings("unchecked")
+    public static Object reduce(Object arrayLike, Object... arguments) {
+        eachParse(arrayLike, arguments[0], (list, sp) -> list.add(sp.getParsed()));
+        System.out.println();
+        return null;
+    }
+
     @SuppressWarnings("unchecked")
     public static Object map(Object arrayLike, Object... arguments) {
         return eachParse(arrayLike, arguments[0], (list, sp) -> list.add(sp.getParsed()));

+ 22 - 11
src/main/java/org/ssssssss/expression/parsing/Ast.java

@@ -986,19 +986,23 @@ public abstract class Ast {
     }
 
     public static class LambdaAccess extends Expression {
-        private final Expression element;
+        private final List<Expression> elements = new ArrayList<>();
         private final Expression function;
         private MemberAccess arrayLike;
 
-        public LambdaAccess (Span span, Expression element, Expression function) {
+        public LambdaAccess (Span span, Expression function, List<Expression> elements) {
             super(span);
-            this.element = element;
+            this.elements.addAll(elements);
+            this.function = function;
+        }
+        public LambdaAccess (Span span, Expression function, Expression... elements) {
+            super(span);
+            this.elements.addAll(Arrays.asList(elements));
             this.function = function;
         }
 
-        /** Returns an expression that must evaluate to a map or array. **/
-        public Expression getElement() {
-            return element;
+        public List<Expression> getElements() {
+            return elements;
         }
 
         /** Returns an expression that is used as the key or index to fetch a map or array element. **/
@@ -1008,10 +1012,10 @@ public abstract class Ast {
         @SuppressWarnings("rawtypes")
         @Override
         public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-            if (ArrayLikeLambdaOneArgumentExecutor.SUPPORT_METHOD.contains(arrayLike.getName().getText())) {
+            if (ArrayLikeLambdaExecutor.SUPPORT_METHOD.contains(arrayLike.getName().getText())) {
                 return oneArgumentParser(template, context);
             } else {
-                ExpressionError.error("只支持 "+ String.join(",", ArrayLikeLambdaOneArgumentExecutor.SUPPORT_METHOD) +"。不支持的lambda函数: " + arrayLike.getName().getText(), arrayLike.getSpan());
+                ExpressionError.error("只支持 "+ String.join(",", ArrayLikeLambdaExecutor.SUPPORT_METHOD) +"。不支持的lambda函数: " + arrayLike.getName().getText(), arrayLike.getSpan());
             }
             return null;
         }
@@ -1024,7 +1028,8 @@ public abstract class Ast {
                 Object arrLikeObj = context.get(parName);
                 if (arrLikeObj.getClass().isArray()) {
                     try {
-                        Integer size = (Integer) arrLikeObj.getClass().getDeclaredField("length").get(arrLikeObj);
+//                        Integer size = (Integer) arrLikeObj.getClass().getDeclaredField("length").get(arrLikeObj);
+                        int size = Array.getLength(arrLikeObj);
                         List<Object> list = new ArrayList<>(size);
                         for (int i = 0; i < size; i++) {
                             list.add(Array.get(arrLikeObj, i));
@@ -1044,15 +1049,21 @@ public abstract class Ast {
                 }
                 if (arrLikeObj instanceof Collection) {
                     Collection<?> coll = (Collection<?>) arrLikeObj;
+                    if (elements.size() > 1) {
+                        return this;
+                    }
                     AtomicInteger ai = new AtomicInteger();
                     return coll.stream().map(o -> ((Supplier) () -> {
                         try {
                             context.push();
-                            context.setOnCurrentScope(getElement().getSpan().getText(), o);
+                            List<Expression> elements = getElements();
+                            for (Expression element : elements) {
+                                context.setOnCurrentScope(element.getSpan().getText(), o);
+                            }
                             context.setOnCurrentScope("_i", ai.getAndIncrement());
                             Object res = function.evaluate(template, context);
                             context.pop();
-                            return new ArrayLikeLambdaOneArgumentExecutor.SourceAndParsed<>(o, res);
+                            return new ArrayLikeLambdaExecutor.SourceAndParsed<>(o, res);
                         } catch (IOException e) {
                             e.printStackTrace();
                             throw new RuntimeException(e);

+ 65 - 7
src/main/java/org/ssssssss/expression/parsing/Parser.java

@@ -130,12 +130,15 @@ public class Parser {
 			return new CharacterLiteral(stream.expect(TokenType.CharacterLiteral).getSpan());
 		} else if (stream.match(TokenType.NullLiteral, false)) {
 			return new NullLiteral(stream.expect(TokenType.NullLiteral).getSpan());
+		} else if (stream.match(TokenType.Lambda, false)) {
+			return parseAccessOrCall(stream,TokenType.Lambda);
 		} else {
 			ExpressionError.error("Expected a variable, field, map, array, function or method call, or literal.", stream);
 			return null; // not reached
 		}
 	}
 
+
 	private static Expression parseMapLiteral (TokenStream stream) {
 		Span openCurly = stream.expect(TokenType.LeftCurly).getSpan();
 
@@ -179,11 +182,26 @@ public class Parser {
 		Span identifier = stream.expect(tokenType).getSpan();
 		Expression result = tokenType == TokenType.StringLiteral ? new StringLiteral(identifier) :new VariableAccess(identifier);
 
+		Expression function = null;
+		Span lpSpan = null;
+		List<Expression> results = null;
 		while (stream.hasMore() && stream.match(false, TokenType.LeftParantheses, TokenType.LeftBracket, TokenType.Period, TokenType.Lambda)) {
-
 			// function or method call
 			if (stream.match(TokenType.LeftParantheses, false)) {
-				List<Expression> arguments = parseArguments(stream);
+				Span ls = stream.expect(TokenType.LeftParantheses).getSpan();
+				List<Expression> arguments = parseArgumentsNotExpect(stream);
+				//多参数lambda
+				if (stream.match(TokenType.RightParantheses, false) && stream.hasNext()) {
+					stream.expect(TokenType.RightParantheses);
+					if (stream.match(TokenType.Lambda, true)) {
+						Expression key = parseExpression(stream);
+						function = new LambdaAccess(new Span(ls, new Span(key.getSpan().getSource(), key.getSpan().getStart(), key.getSpan().getEnd() + 1)), key, arguments);
+						arguments.clear();
+						arguments.add(function);
+					} else {
+						stream.prev();
+					}
+				}
 				Span closingSpan = stream.expect(TokenType.RightParantheses).getSpan();
 				if (result instanceof VariableAccess || result instanceof MapOrArrayAccess) {
 					result = new FunctionCall(new Span(result.getSpan(), closingSpan), result, arguments);
@@ -196,8 +214,8 @@ public class Parser {
 					}
 					MethodCall methodCall = new MethodCall(new Span(result.getSpan(), closingSpan), (MemberAccess) result, arguments);
 					String name = ((MemberAccess) result).getName().getText();
-					if (ArrayLikeLambdaOneArgumentExecutor.SUPPORT_METHOD.contains(name)) {
-						methodCall.setCachedMethod(ArrayLikeLambdaOneArgumentExecutor.METHODS.get(name));
+					if (ArrayLikeLambdaExecutor.SUPPORT_METHOD.contains(name)) {
+						methodCall.setCachedMethod(ArrayLikeLambdaExecutor.METHODS.get(name));
 						methodCall.setCachedMethodStatic(true);
 					}
 					result = methodCall;
@@ -221,8 +239,7 @@ public class Parser {
 
 			else if (stream.match(TokenType.Lambda, true)) {
 				Expression key = parseExpression(stream);
-//				Span closingSpan = stream.expect(TokenType.RightParantheses).getSpan();
-				result = new LambdaAccess(new Span(result.getSpan(), key.getSpan()), result, key);
+				result = new LambdaAccess(new Span(result.getSpan(), key.getSpan()), key, result);
 			}
 		}
 
@@ -232,13 +249,54 @@ public class Parser {
 	/** Does not consume the closing parentheses. **/
 	private static List<Expression> parseArguments (TokenStream stream) {
 		stream.expect(TokenType.LeftParantheses);
+		return parseArgumentsNotExpect(stream);
+	}
+	private static List<Expression> parseArgumentsNotExpect (TokenStream stream) {
 		List<Expression> arguments = new ArrayList<Expression>();
 		while (stream.hasMore() && !stream.match(TokenType.RightParantheses, false)) {
+			boolean f1 = false, f2 = false;
+			int c = 0;
+			while (stream.hasNext()) {
+				c++;
+				stream.next();
+				if (f1 && stream.match(TokenType.Lambda, false)) {
+					f2 = true;
+					int count = c;
+					for (int i = 0; i < count - 1; i++) {
+						stream.prev();
+						c--;
+					}
+					break;
+				}
+				if (stream.match(TokenType.RightParantheses, false)) {
+					f1 = true;
+				} else {
+					f1 = false;
+				}
+			}
+			if (!f2) {
+				int count = c;
+				for (int i = 0; i < count; i++) {
+					stream.prev();
+					c--;
+				}
+			}
 			arguments.add(parseExpression(stream));
+			if (f1 && f2) {
+				while (stream.match(TokenType.Comma, true)) {
+					arguments.add(parseExpression(stream));
+				}
+				return arguments;
+			}
 			if (!stream.match(TokenType.RightParantheses, false)) {
-				stream.expect(TokenType.Comma);
+//				if (stream.match(TokenType.Lambda, false)) {
+//					return arguments;
+//				} else {
+					stream.expect(TokenType.Comma);
+//				}
 			}
 		}
 		return arguments;
 	}
+
 }