/*
* Copyright 2011 The authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.lang.ognl.parsing;
import com.intellij.lang.ognl.psi.OgnlTokenTypes;
import com.intellij.lang.pratt.PrattBuilder;
import com.intellij.lang.pratt.PrattParser;
import com.intellij.lang.pratt.ReducingParser;
import com.intellij.lang.pratt.TokenParser;
import com.intellij.patterns.IElementTypePattern;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import static com.intellij.lang.ognl.psi.OgnlTokenTypes.*;
import static com.intellij.lang.pratt.PathPattern.path;
import static com.intellij.lang.pratt.PrattRegistry.registerParser;
import static com.intellij.patterns.PlatformPatterns.elementType;
/**
* @author Yann Cébron
*/
public class OgnlParser extends PrattParser {
public static final int INITIAL_LEVEL = 0;
public static final int EXPR_LEVEL = INITIAL_LEVEL + 10;
public static final int EXPR_BIT_LEVEL = EXPR_LEVEL + 10;
public static final int COMP_LEVEL = EXPR_BIT_LEVEL + 10;
public static final int EQ_LEVEL = COMP_LEVEL + 10;
public static final int EQ_COMP_LEVEL = EQ_LEVEL + 10;
public static final int NUMBERS_LEVEL = EQ_COMP_LEVEL + 10;
public static final int UNARY_LEVEL = NUMBERS_LEVEL + 20;
public static final int ATOM_LEVEL = UNARY_LEVEL + 20;
private static final IElementTypePattern IDENTIFIER_PATTERN = elementType().or(OgnlElementTypes.REFERENCE_EXPRESSION,
OgnlElementTypes.VARIABLE_EXPRESSION);
static {
// "%{" + matching "}"
registerParser(EXPRESSION_START, INITIAL_LEVEL, new ReducingParser() {
@Override
public IElementType parseFurther(final PrattBuilder builder) {
parseExpression(builder);
builder.assertToken(EXPRESSION_END, "'}' expected");
return OgnlElementTypes.EXPRESSION_HOLDER;
}
});
// boolean ops
registerParser(OR_KEYWORD,
EXPR_LEVEL + 2,
path().left(),
expression(EXPR_LEVEL + 2, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(OR_OR,
EXPR_LEVEL + 2,
path().left(),
expression(EXPR_LEVEL + 2, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(AND_KEYWORD,
EXPR_LEVEL + 3,
path().left(),
expression(EXPR_LEVEL + 3, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(AND_AND,
EXPR_LEVEL + 3,
path().left(),
expression(EXPR_LEVEL + 3, OgnlElementTypes.BINARY_EXPRESSION));
// bitwise boolean ops
registerParser(OR,
EXPR_BIT_LEVEL + 2,
path().left(),
expression(EXPR_BIT_LEVEL + 2, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(BOR_KEYWORD,
EXPR_BIT_LEVEL + 2,
path().left(),
expression(EXPR_BIT_LEVEL + 2, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(AND,
EXPR_BIT_LEVEL + 3,
path().left(),
expression(EXPR_BIT_LEVEL + 3, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(BAND_KEYWORD,
EXPR_BIT_LEVEL + 3,
path().left(),
expression(EXPR_BIT_LEVEL + 3, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(XOR, EXPR_BIT_LEVEL, path().left(), expression(EXPR_BIT_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(XOR_KEYWORD,
EXPR_BIT_LEVEL,
path().left(),
expression(EXPR_BIT_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
// bitwise shift ops
registerParser(SHIFT_LEFT,
EXPR_BIT_LEVEL,
path().left(),
expression(EXPR_BIT_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(SHIFT_LEFT_KEYWORD,
EXPR_BIT_LEVEL,
path().left(),
expression(EXPR_BIT_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(SHIFT_RIGHT,
EXPR_BIT_LEVEL,
path().left(),
expression(EXPR_BIT_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(SHIFT_RIGHT_KEYWORD,
EXPR_BIT_LEVEL,
path().left(),
expression(EXPR_BIT_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(SHIFT_RIGHT_LOGICAL,
EXPR_BIT_LEVEL,
path().left(),
expression(EXPR_BIT_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(SHIFT_RIGHT_LOGICAL_KEYWORD,
EXPR_BIT_LEVEL,
path().left(),
expression(EXPR_BIT_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
// equals/comparison
registerParser(EQUAL, EQ_LEVEL, path().left(), expression(EQ_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(EQ_KEYWORD, EQ_LEVEL, path().left(), expression(EQ_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(NOT_EQUAL, EQ_LEVEL, path().left(), expression(EQ_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(NEQ_KEYWORD, EQ_LEVEL, path().left(), expression(EQ_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(LESS, EQ_COMP_LEVEL, path().left(), expression(EQ_COMP_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(LT_KEYWORD,
EQ_COMP_LEVEL,
path().left(),
expression(EQ_COMP_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(LESS_EQUAL,
EQ_COMP_LEVEL,
path().left(),
expression(EQ_COMP_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(LT_EQ_KEYWORD,
EQ_COMP_LEVEL,
path().left(),
expression(EQ_COMP_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(GREATER,
EQ_COMP_LEVEL,
path().left(),
expression(EQ_COMP_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(GT_KEYWORD,
EQ_COMP_LEVEL,
path().left(),
expression(EQ_COMP_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(GREATER_EQUAL,
EQ_COMP_LEVEL,
path().left(),
expression(EQ_COMP_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(GT_EQ_KEYWORD,
EQ_COMP_LEVEL,
path().left(),
expression(EQ_COMP_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
// operations
registerParser(PLUS,
NUMBERS_LEVEL + 7,
path().left(),
expression(NUMBERS_LEVEL + 7, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(PLUS,
UNARY_LEVEL + 1,
path().up(),
expression(UNARY_LEVEL, OgnlElementTypes.UNARY_EXPRESSION)); // TODO needed?
registerParser(MINUS,
NUMBERS_LEVEL + 7,
path().left(),
expression(NUMBERS_LEVEL + 7, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(MINUS, UNARY_LEVEL + 1, path().up(), expression(UNARY_LEVEL, OgnlElementTypes.UNARY_EXPRESSION));
registerParser(MULTIPLY,
NUMBERS_LEVEL + 8,
path().left(),
expression(NUMBERS_LEVEL + 8, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(DIVISION,
NUMBERS_LEVEL + 8,
path().left(),
expression(NUMBERS_LEVEL + 8, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(MODULO,
NUMBERS_LEVEL + 9,
path().left(),
expression(NUMBERS_LEVEL + 9, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(NEGATE,
UNARY_LEVEL + 1,
path().up(),
expression(UNARY_LEVEL + 1, OgnlElementTypes.UNARY_EXPRESSION));
registerParser(NOT, UNARY_LEVEL + 1, path().up(), expression(UNARY_LEVEL + 1, OgnlElementTypes.UNARY_EXPRESSION));
registerParser(NOT_KEYWORD,
UNARY_LEVEL + 1,
path().up(),
expression(UNARY_LEVEL + 1, OgnlElementTypes.UNARY_EXPRESSION));
// literals TODO detect missing closing quote/tick
registerParser(OgnlTokenTypes.CHARACTER_LITERAL,
ATOM_LEVEL + 1,
path().up(),
TokenParser.postfix(OgnlElementTypes.STRING_LITERAL));
registerParser(STRING_LITERAL,
ATOM_LEVEL + 1,
path().up(),
TokenParser.postfix(OgnlElementTypes.STRING_LITERAL));
registerParser(INTEGER_LITERAL,
ATOM_LEVEL + 1,
path().up(),
TokenParser.postfix(OgnlElementTypes.INTEGER_LITERAL));
registerParser(BIG_INTEGER_LITERAL,
ATOM_LEVEL + 1,
path().up(),
TokenParser.postfix(OgnlElementTypes.BIG_INTEGER_LITERAL));
registerParser(DOUBLE_LITERAL,
ATOM_LEVEL + 1,
path().up(),
TokenParser.postfix(OgnlElementTypes.DOUBLE_LITERAL));
registerParser(BIG_DECIMAL_LITERAL,
ATOM_LEVEL + 1,
path().up(),
TokenParser.postfix(OgnlElementTypes.BIG_DECIMAL_LITERAL));
registerParser(FALSE_KEYWORD, ATOM_LEVEL + 1, path().up(), TokenParser.postfix(OgnlElementTypes.BOOLEAN_LITERAL));
registerParser(TRUE_KEYWORD, ATOM_LEVEL + 1, path().up(), TokenParser.postfix(OgnlElementTypes.BOOLEAN_LITERAL));
registerParser(NULL_KEYWORD, ATOM_LEVEL + 1, path().up(), TokenParser.postfix(OgnlElementTypes.NULL_LITERAL));
// (...): parenthesized expression
registerParser(LPARENTH, ATOM_LEVEL + 1, path().up(), new ReducingParser() {
@NotNull
public IElementType parseFurther(@NotNull final PrattBuilder builder) {
parseExpression(builder);
builder.assertToken(RPARENTH, "')' expected");
return OgnlElementTypes.PARENTHESIZED_EXPRESSION;
}
});
// [...]: indexed expression, only after identifier/var
registerParser(LBRACKET, EXPR_LEVEL + 1, path().left(IDENTIFIER_PATTERN).up(),
new ReducingParser() {
@Override
public IElementType parseFurther(final PrattBuilder builder) {
parseExpression(builder);
builder.assertToken(RBRACKET, "']' expected");
return OgnlElementTypes.INDEXED_EXPRESSION;
}
});
// { a,b,c } list expression
registerParser(LBRACE, EXPR_LEVEL + 1, path().up(), new ReducingParser() {
@Override
public IElementType parseFurther(final PrattBuilder builder) {
parseExpression(builder);
if (builder.assertToken(COMMA, "sequence expected")) {
do {
parseExpression(builder);
} while (builder.checkToken(COMMA));
}
builder.assertToken(RBRACE, "'}' expected");
return OgnlElementTypes.SEQUENCE_EXPRESSION;
}
});
// special stuff ============================
// condition ? then : else
registerParser(QUESTION, EXPR_LEVEL + 1, new ReducingParser() {
@Override
public IElementType parseFurther(final PrattBuilder builder) {
builder.createChildBuilder(EXPR_LEVEL, "'then' expression expected").parse();
builder.assertToken(COLON, "':' expected");
builder.createChildBuilder(EXPR_LEVEL, "'else' expression expected").parse();
return OgnlElementTypes.CONDITIONAL_EXPRESSION;
}
});
// "X in ...", "X not in ..." TODO require sequence expr
registerParser(IN_KEYWORD,
EXPR_LEVEL + 1,
path().left(),
expression(EXPR_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
registerParser(NOT_IN_KEYWORD,
EXPR_LEVEL + 1,
path().left(),
expression(EXPR_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
// TODO new
// instanceof
registerParser(INSTANCEOF_KEYWORD,
EXPR_LEVEL + 1,
path().left(),
expression(EXPR_LEVEL, OgnlElementTypes.BINARY_EXPRESSION));
// method calls: reference([paramA, paramB, ..])
registerParser(LPARENTH,
EXPR_LEVEL + 1,
path().left(OgnlElementTypes.REFERENCE_EXPRESSION).up(),
new ReducingParser() {
@Override
public IElementType parseFurther(final PrattBuilder builder) {
if (!builder.checkToken(RPARENTH)) {
parseExpression(builder);
while (builder.checkToken(COMMA)) {
parseExpression(builder);
}
builder.assertToken(RPARENTH, "')' expected");
}
return OgnlElementTypes.METHOD_CALL_EXPRESSION;
}
});
// TODO static method calls @class@method
// TODO static field ref @class@field
// TODO projection/selection: e1.{e2} / e1.{?e2}
// TODO sub-expression: e1.(e2)
// TODO chained sub-expression: headline.parent.(ensureLoaded(), name)
// TODO list creation: { e, ... } --> already via sequence?
// TODO array creation: new array-component-class[] { e, ... }
// TODO map creation: #{ e1 : e2, ... }
// TODO map creation w/ class: #@classname@{ e1 : e2, ... }
// TODO lambda: :[ e ]
// #var
registerParser(HASH, ATOM_LEVEL + 1, path().up(), new ReducingParser() {
@Override
public IElementType parseFurther(final PrattBuilder builder) {
builder.assertToken(IDENTIFIER, "Variable identifier expected");
return OgnlElementTypes.VARIABLE_EXPRESSION;
}
});
// TODO #var = value
// reference ("plain")
registerParser(IDENTIFIER, ATOM_LEVEL + 1, path().up(), TokenParser.postfix(OgnlElementTypes.REFERENCE_EXPRESSION));
// nested references (a.b.c) TODO check PSI nesting
registerParser(DOT, EXPR_LEVEL + 1, path().left(), new ReducingParser() {
@NotNull
public IElementType parseFurther(@NotNull final PrattBuilder builder) {
builder.assertToken(IDENTIFIER, "Nested expression expected");
return OgnlElementTypes.REFERENCE_EXPRESSION;
}
});
}
private static TokenParser expression(final int priority, final OgnlElementType type) {
return TokenParser.infix(priority, type, "Expression expected");
}
public static IElementType parseExpression(final PrattBuilder builder) {
return builder.createChildBuilder(EXPR_LEVEL, "Expression expected").parse();
}
}