| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * 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.codeInsight.template.emmet; |
| |
| import com.intellij.codeInsight.template.CustomTemplateCallback; |
| import com.intellij.codeInsight.template.emmet.generators.ZenCodingGenerator; |
| import com.intellij.codeInsight.template.emmet.nodes.*; |
| import com.intellij.codeInsight.template.emmet.tokens.*; |
| import com.intellij.codeInsight.template.impl.TemplateImpl; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.List; |
| |
| /** |
| * User: zolotov |
| * Date: 1/25/13 |
| */ |
| public abstract class EmmetParser { |
| private final List<ZenCodingToken> myTokens; |
| protected final CustomTemplateCallback myCallback; |
| protected final ZenCodingGenerator myGenerator; |
| |
| private int myIndex = 0; |
| |
| public EmmetParser(List<ZenCodingToken> tokens, CustomTemplateCallback callback, ZenCodingGenerator generator) { |
| myTokens = tokens; |
| myCallback = callback; |
| myGenerator = generator; |
| } |
| |
| public int getIndex() { |
| return myIndex; |
| } |
| |
| @Nullable |
| public ZenCodingNode parse() { |
| ZenCodingNode add = parseAddOrMore(); |
| if (add == null) { |
| return null; |
| } |
| |
| ZenCodingNode result = add; |
| |
| while (true) { |
| ZenCodingToken token = getToken(); |
| if (token != ZenCodingTokens.PIPE) { |
| return result; |
| } |
| |
| advance(); |
| token = getToken(); |
| if (!(token instanceof IdentifierToken)) { |
| return null; |
| } |
| |
| final String filterSuffix = ((IdentifierToken)token).getText(); |
| if (ZenCodingUtil.checkFilterSuffix(filterSuffix)) { |
| result = new FilterNode(result, filterSuffix); |
| } |
| |
| advance(); |
| } |
| } |
| |
| @Nullable |
| protected ZenCodingNode parseAddOrMore() { |
| ZenCodingNode mul = parseMul(); |
| |
| ZenCodingToken operationToken = getToken(); |
| if (operationToken == ZenCodingTokens.OPENING_R_BRACKET) { |
| mul = new MoreOperationNode(notNullNode(mul), parseExpression()); |
| operationToken = getToken(); |
| } |
| if (!(operationToken instanceof OperationToken)) { |
| return mul; |
| } |
| char sign = ((OperationToken)operationToken).getSign(); |
| |
| if (sign == '^') { |
| return parseClimbUpOperation(mul); |
| } |
| if (sign == '+') { |
| advance(); |
| ZenCodingNode add2 = parseAddOrMore(); |
| if (add2 == null) { |
| return mul; |
| } |
| return new AddOperationNode(notNullNode(mul), add2); |
| } |
| if (sign == '>') { |
| return parseMoreOperation(mul); |
| } |
| return null; |
| } |
| |
| protected ZenCodingNode parseClimbUpOperation(@Nullable ZenCodingNode leftPart) { |
| advance(); |
| leftPart = notNullNode(leftPart); |
| ZenCodingNode rightPart = parseAddOrMore(); |
| if (rightPart == null) { |
| return leftPart; |
| } |
| return new ClimbUpOperationNode(leftPart, rightPart); |
| } |
| |
| protected ZenCodingNode parseMoreOperation(@Nullable ZenCodingNode leftPart) { |
| advance(); |
| leftPart = notNullNode(leftPart); |
| ZenCodingNode rightPart = parseAddOrMore(); |
| if (rightPart == null) { |
| return leftPart; |
| } |
| return new MoreOperationNode(leftPart, rightPart); |
| } |
| |
| private static ZenCodingNode notNullNode(ZenCodingNode node) { |
| return node != null ? node : ZenEmptyNode.INSTANCE; |
| } |
| |
| protected int advance() { |
| return myIndex++; |
| } |
| |
| @Nullable |
| private ZenCodingNode parseMul() { |
| ZenCodingNode exp = parseExpression(); |
| if (exp == null) { |
| return null; |
| } |
| ZenCodingToken operationToken = getToken(); |
| if (!(operationToken instanceof OperationToken)) { |
| return exp; |
| } |
| if (((OperationToken)operationToken).getSign() != '*') { |
| return exp; |
| } |
| advance(); |
| ZenCodingToken numberToken = getToken(); |
| if (numberToken instanceof NumberToken) { |
| advance(); |
| return new MulOperationNode(exp, ((NumberToken)numberToken).getNumber()); |
| } |
| return new UnaryMulOperationNode(exp); |
| } |
| |
| @Nullable |
| private ZenCodingNode parseExpression() { |
| ZenCodingToken token = getToken(); |
| if (token == ZenCodingTokens.OPENING_R_BRACKET) { |
| advance(); |
| ZenCodingNode add = parseAddOrMore(); |
| if (add == null) { |
| return null; |
| } |
| ZenCodingToken closingBrace = getToken(); |
| if (closingBrace != ZenCodingTokens.CLOSING_R_BRACKET) { |
| return null; |
| } |
| advance(); |
| return add; |
| } |
| else if (token instanceof TextToken) { |
| advance(); |
| return new TextNode((TextToken)token); |
| } |
| |
| final ZenCodingNode templateNode = parseTemplate(); |
| if (templateNode == null) { |
| return null; |
| } |
| |
| token = getToken(); |
| if (token instanceof TextToken) { |
| advance(); |
| return new MoreOperationNode(templateNode, new TextNode((TextToken)token)); |
| } |
| return templateNode; |
| } |
| |
| @Nullable |
| protected ZenCodingNode parseTemplate() { |
| ZenCodingToken token = getToken(); |
| if (!(token instanceof IdentifierToken)) { |
| return null; |
| } |
| String templateKey = ((IdentifierToken)token).getText(); |
| advance(); |
| |
| TemplateImpl template = myCallback.findApplicableTemplate(templateKey); |
| if (template == null && !ZenCodingUtil.isXML11ValidQName(templateKey)) { |
| return null; |
| } |
| |
| final TemplateToken templateToken = new TemplateToken(templateKey); |
| if (!setTemplate(templateToken, template)) { |
| return null; |
| } |
| return new TemplateNode(templateToken); |
| } |
| |
| protected boolean setTemplate(final TemplateToken token, TemplateImpl template) { |
| if (template == null) { |
| template = myGenerator.createTemplateByKey(token.getKey()); |
| } |
| if (template == null) { |
| return false; |
| } |
| return token.setTemplate(template, myCallback); |
| } |
| |
| @Nullable |
| protected ZenCodingToken getToken() { |
| if (myIndex < myTokens.size()) { |
| return myTokens.get(myIndex); |
| } |
| return null; |
| } |
| } |