| /******************************************************************************* |
| * Copyright (c) 2009, 2010 Cloudsmith Inc. and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Cloudsmith Inc. - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.equinox.internal.p2.metadata.expression.parser; |
| |
| import java.util.*; |
| import org.eclipse.equinox.internal.p2.metadata.expression.Variable; |
| import org.eclipse.equinox.p2.metadata.expression.IExpression; |
| import org.eclipse.equinox.p2.metadata.expression.IExpressionFactory; |
| |
| public class QLParser extends ExpressionParser { |
| private static final long serialVersionUID = 882034383978853143L; |
| |
| private static final int TOKEN_ANY = 42; |
| |
| private static final int TOKEN_LATEST = 70; |
| private static final int TOKEN_LIMIT = 71; |
| private static final int TOKEN_FIRST = 72; |
| private static final int TOKEN_FLATTEN = 73; |
| private static final int TOKEN_UNIQUE = 74; |
| private static final int TOKEN_SELECT = 75; |
| private static final int TOKEN_COLLECT = 76; |
| private static final int TOKEN_TRAVERSE = 77; |
| private static final int TOKEN_INTERSECT = 78; |
| private static final int TOKEN_UNION = 79; |
| |
| private static final Map<String, Integer> qlKeywords; |
| static { |
| qlKeywords = new HashMap<String, Integer>(); |
| qlKeywords.putAll(keywords); |
| qlKeywords.put(KEYWORD_COLLECT, new Integer(TOKEN_COLLECT)); |
| qlKeywords.put(KEYWORD_FALSE, new Integer(TOKEN_FALSE)); |
| qlKeywords.put(KEYWORD_FIRST, new Integer(TOKEN_FIRST)); |
| qlKeywords.put(KEYWORD_FLATTEN, new Integer(TOKEN_FLATTEN)); |
| qlKeywords.put(KEYWORD_LATEST, new Integer(TOKEN_LATEST)); |
| qlKeywords.put(KEYWORD_LIMIT, new Integer(TOKEN_LIMIT)); |
| qlKeywords.put(KEYWORD_NULL, new Integer(TOKEN_NULL)); |
| qlKeywords.put(KEYWORD_SELECT, new Integer(TOKEN_SELECT)); |
| qlKeywords.put(KEYWORD_TRAVERSE, new Integer(TOKEN_TRAVERSE)); |
| qlKeywords.put(KEYWORD_TRUE, new Integer(TOKEN_TRUE)); |
| qlKeywords.put(KEYWORD_UNIQUE, new Integer(TOKEN_UNIQUE)); |
| qlKeywords.put(KEYWORD_INTERSECT, new Integer(TOKEN_INTERSECT)); |
| qlKeywords.put(KEYWORD_UNION, new Integer(TOKEN_UNION)); |
| qlKeywords.put(OPERATOR_EACH, new Integer(TOKEN_ANY)); |
| } |
| |
| public QLParser(IExpressionFactory factory) { |
| super(factory); |
| } |
| |
| protected Map<String, Integer> keywordToTokenMap() { |
| return qlKeywords; |
| } |
| |
| protected IExpression parseCondition() { |
| IExpression expr = parseOr(); |
| if (currentToken == TOKEN_IF) { |
| nextToken(); |
| IExpression ifTrue = parseOr(); |
| assertToken(TOKEN_ELSE); |
| nextToken(); |
| expr = factory.condition(expr, ifTrue, parseOr()); |
| } |
| return expr; |
| } |
| |
| protected IExpression parseMember() { |
| IExpression expr = parseFunction(); |
| String name; |
| while (currentToken == TOKEN_DOT || currentToken == TOKEN_LB) { |
| int savePos = tokenPos; |
| int saveToken = currentToken; |
| Object saveTokenValue = tokenValue; |
| nextToken(); |
| if (saveToken == TOKEN_DOT) { |
| switch (currentToken) { |
| case TOKEN_IDENTIFIER : |
| name = (String) tokenValue; |
| nextToken(); |
| if (currentToken == TOKEN_LP) { |
| nextToken(); |
| IExpression[] callArgs = parseArray(); |
| assertToken(TOKEN_RP); |
| nextToken(); |
| expr = factory.memberCall(expr, name, callArgs); |
| } else |
| expr = factory.member(expr, name); |
| break; |
| |
| default : |
| tokenPos = savePos; |
| currentToken = saveToken; |
| tokenValue = saveTokenValue; |
| return expr; |
| } |
| } else { |
| IExpression atExpr = parseMember(); |
| assertToken(TOKEN_RB); |
| nextToken(); |
| expr = factory.at(expr, atExpr); |
| } |
| } |
| return expr; |
| } |
| |
| protected IExpression parseFunction() { |
| if (currentToken == TOKEN_IDENTIFIER) { |
| Object function = factory.getFunctionMap().get(tokenValue); |
| if (function != null) { |
| int savePos = tokenPos; |
| int saveToken = currentToken; |
| Object saveTokenValue = tokenValue; |
| |
| nextToken(); |
| if (currentToken == TOKEN_LP) { |
| nextToken(); |
| IExpression[] args = currentToken == TOKEN_RP ? IExpressionFactory.NO_ARGS : parseArray(); |
| assertToken(TOKEN_RP); |
| nextToken(); |
| return factory.function(function, args); |
| } |
| tokenPos = savePos; |
| currentToken = saveToken; |
| tokenValue = saveTokenValue; |
| } |
| } |
| return parseUnary(); |
| } |
| |
| protected IExpression parseCollectionLHS() { |
| IExpression expr; |
| switch (currentToken) { |
| case TOKEN_SELECT : |
| case TOKEN_COLLECT : |
| case TOKEN_FIRST : |
| case TOKEN_FLATTEN : |
| case TOKEN_TRAVERSE : |
| case TOKEN_LATEST : |
| case TOKEN_LIMIT : |
| case TOKEN_INTERSECT : |
| case TOKEN_UNION : |
| case TOKEN_UNIQUE : |
| expr = getVariableOrRootMember(rootVariable); |
| break; |
| default : |
| expr = super.parseCollectionLHS(); |
| } |
| return expr; |
| } |
| |
| protected IExpression parseCollectionRHS(IExpression expr, int funcToken) { |
| switch (funcToken) { |
| case TOKEN_SELECT : |
| expr = factory.select(expr, parseLambdaDefinition()); |
| break; |
| case TOKEN_COLLECT : |
| expr = factory.collect(expr, parseLambdaDefinition()); |
| break; |
| case TOKEN_FIRST : |
| expr = factory.first(expr, parseLambdaDefinition()); |
| break; |
| case TOKEN_TRAVERSE : |
| expr = factory.traverse(expr, parseLambdaDefinition()); |
| break; |
| case TOKEN_LATEST : |
| if (currentToken == TOKEN_RP) { |
| expr = factory.latest(expr); |
| assertToken(TOKEN_RP); |
| nextToken(); |
| } else |
| expr = factory.latest(factory.select(expr, parseLambdaDefinition())); |
| break; |
| case TOKEN_FLATTEN : |
| if (currentToken == TOKEN_RP) { |
| expr = factory.flatten(expr); |
| assertToken(TOKEN_RP); |
| nextToken(); |
| } else |
| expr = factory.flatten(factory.select(expr, parseLambdaDefinition())); |
| break; |
| case TOKEN_LIMIT : |
| expr = factory.limit(expr, parseCondition()); |
| assertToken(TOKEN_RP); |
| nextToken(); |
| break; |
| case TOKEN_INTERSECT : |
| expr = factory.intersect(expr, parseCondition()); |
| assertToken(TOKEN_RP); |
| nextToken(); |
| break; |
| case TOKEN_UNION : |
| expr = factory.union(expr, parseCondition()); |
| assertToken(TOKEN_RP); |
| nextToken(); |
| break; |
| case TOKEN_UNIQUE : |
| if (currentToken == TOKEN_RP) |
| expr = factory.unique(expr, factory.constant(null)); |
| else { |
| expr = factory.unique(expr, parseMember()); |
| assertToken(TOKEN_RP); |
| nextToken(); |
| } |
| break; |
| default : |
| expr = super.parseCollectionRHS(expr, funcToken); |
| } |
| return expr; |
| } |
| |
| protected IExpression parseUnary() { |
| IExpression expr; |
| switch (currentToken) { |
| case TOKEN_LB : |
| nextToken(); |
| expr = factory.array(parseArray()); |
| assertToken(TOKEN_RB); |
| nextToken(); |
| break; |
| case TOKEN_ANY : |
| expr = factory.variable(OPERATOR_EACH); |
| nextToken(); |
| break; |
| default : |
| expr = super.parseUnary(); |
| } |
| return expr; |
| } |
| |
| protected IExpression parseLambdaDefinition() { |
| boolean endingRC = false; |
| int anyIndex = -1; |
| IExpression[] initializers = IExpressionFactory.NO_ARGS; |
| IExpression[] variables; |
| if (currentToken == TOKEN_LC) { |
| // Lambda starts without currying. |
| endingRC = true; |
| nextToken(); |
| anyIndex = 0; |
| variables = parseVariables(); |
| if (variables == null) |
| // empty means no pipe at the end. |
| throw syntaxError(); |
| } else { |
| anyIndex = 0; |
| variables = parseVariables(); |
| if (variables == null) { |
| anyIndex = -1; |
| initializers = parseArray(); |
| assertToken(TOKEN_LC); |
| nextToken(); |
| endingRC = true; |
| for (int idx = 0; idx < initializers.length; ++idx) { |
| IExpression initializer = initializers[idx]; |
| if (initializer instanceof Variable && OPERATOR_EACH.equals(initializer.toString())) { |
| if (anyIndex == -1) |
| anyIndex = idx; |
| else |
| anyIndex = -1; // Second Each. This is illegal |
| break; |
| } |
| } |
| if (anyIndex == -1) |
| throw new IllegalArgumentException("Exaclty one _ must be present among the currying expressions"); //$NON-NLS-1$ |
| |
| variables = parseVariables(); |
| if (variables == null) |
| // empty means no pipe at the end. |
| throw syntaxError(); |
| } |
| |
| } |
| nextToken(); |
| IExpression body = parseCondition(); |
| if (endingRC) { |
| assertToken(TOKEN_RC); |
| nextToken(); |
| } |
| |
| assertToken(TOKEN_RP); |
| nextToken(); |
| IExpression each; |
| IExpression[] assignments; |
| if (initializers.length == 0) { |
| if (variables.length != 1) |
| throw new IllegalArgumentException("Must have exactly one variable unless currying is used"); //$NON-NLS-1$ |
| each = variables[0]; |
| assignments = IExpressionFactory.NO_ARGS; |
| } else { |
| if (initializers.length != variables.length) |
| throw new IllegalArgumentException("Number of currying expressions and variables differ"); //$NON-NLS-1$ |
| |
| if (initializers.length == 1) { |
| // This is just a map from _ to some variable |
| each = variables[0]; |
| assignments = IExpressionFactory.NO_ARGS; |
| } else { |
| int idx; |
| each = variables[anyIndex]; |
| assignments = new IExpression[initializers.length - 1]; |
| for (idx = 0; idx < anyIndex; ++idx) |
| assignments[idx] = factory.assignment(variables[idx], initializers[idx]); |
| for (++idx; idx < initializers.length; ++idx) |
| assignments[idx] = factory.assignment(variables[idx], initializers[idx]); |
| } |
| } |
| return factory.lambda(each, assignments, body); |
| } |
| |
| private IExpression[] parseVariables() { |
| int savePos = tokenPos; |
| int saveToken = currentToken; |
| Object saveTokenValue = tokenValue; |
| List<Object> ids = null; |
| while (currentToken == TOKEN_IDENTIFIER) { |
| if (ids == null) |
| ids = new ArrayList<Object>(); |
| ids.add(tokenValue); |
| nextToken(); |
| if (currentToken == TOKEN_COMMA) { |
| nextToken(); |
| continue; |
| } |
| break; |
| } |
| |
| if (currentToken != TOKEN_PIPE) { |
| // This was not a variable list |
| tokenPos = savePos; |
| currentToken = saveToken; |
| tokenValue = saveTokenValue; |
| return null; |
| } |
| |
| if (ids == null) |
| // Empty list but otherwise OK |
| return IExpressionFactory.NO_ARGS; |
| |
| int top = ids.size(); |
| IExpression[] result = new IExpression[top]; |
| for (int idx = 0; idx < top; ++idx) { |
| String name = (String) ids.get(idx); |
| IExpression var = factory.variable(name); |
| push(var); |
| result[idx] = var; |
| } |
| return result; |
| } |
| } |