/* * Copyright 2011-present Greg Shrago * * 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 org.intellij.grammar.generator; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.ContainerUtil; import org.intellij.grammar.KnownAttribute; import org.intellij.grammar.psi.BnfExpression; import org.intellij.grammar.psi.BnfRule; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; import static java.lang.String.format; import static org.intellij.grammar.generator.ExpressionHelper.OperatorInfo; import static org.intellij.grammar.generator.ExpressionHelper.OperatorType; import static org.intellij.grammar.generator.ParserGeneratorUtil.*; /** * @author greg */ public class ExpressionGeneratorHelper { private static final ConsumeType CONSUME_TYPE_OVERRIDE = ConsumeType.SMART; public static void generateExpressionRoot(ExpressionHelper.ExpressionInfo info, ParserGenerator g) { Map<String, List<OperatorInfo>> opCalls = ContainerUtil.newLinkedHashMap(); for (BnfRule rule : info.priorityMap.keySet()) { OperatorInfo operator = info.operatorMap.get(rule); String opCall = g.generateNodeCall(info.rootRule, operator.operator, getNextName(getFuncName(operator.rule), 0), CONSUME_TYPE_OVERRIDE); List<OperatorInfo> list = opCalls.get(opCall); if (list == null) opCalls.put(opCall, list = ContainerUtil.newArrayListWithCapacity(2)); list.add(operator); } Set<String> sortedOpCalls = opCalls.keySet(); for (String s : info.toString().split("\n")) { g.out("// " + s); } // main entry String methodName = getFuncName(info.rootRule); String kernelMethodName = getNextName(methodName, 0); String frameName = quote(ParserGeneratorUtil.getRuleDisplayName(info.rootRule, true)); g.out("public static boolean %s(PsiBuilder %s, int %s, int %s) {", methodName, g.N.builder, g.N.level, g.N.priority); g.out("if (!recursion_guard_(%s, %s, \"%s\")) return false;", g.N.builder, g.N.level, methodName); if (frameName != null) { g.out("addVariant(%s, %s);", g.N.builder, frameName); } g.generateFirstCheck(info.rootRule, frameName, true); g.out("boolean %s, %s;", g.N.result, g.N.pinned); g.out("Marker %s = enter_section_(%s, %s, _NONE_, %s);", g.N.marker, g.N.builder, g.N.level, frameName); boolean first = true; for (String opCall : sortedOpCalls) { OperatorInfo operator = ContainerUtil.getFirstItem(findOperators(opCalls.get(opCall), OperatorType.ATOM, OperatorType.PREFIX)); if (operator == null) continue; String nodeCall = g.generateNodeCall(operator.rule, null, getFuncName(operator.rule)); g.out("%s%s = %s;", first ? "" : format("if (!%s) ", g.N.result), g.N.result, nodeCall); first = false; } g.out("%s = %s;", g.N.pinned, g.N.result); g.out("%s = %s && %s(%s, %s + 1, %s);", g.N.result, g.N.result, kernelMethodName, g.N.builder, g.N.level, g.N.priority); g.out("exit_section_(%s, %s, %s, null, %s, %s, null);", g.N.builder, g.N.level, g.N.marker, g.N.result, g.N.pinned); g.out("return %s || %s;", g.N.result, g.N.pinned); g.out("}"); g.newLine(); // kernel g.out("public static boolean %s(PsiBuilder %s, int %s, int %s) {", kernelMethodName, g.N.builder, g.N.level, g.N.priority); g.out("if (!recursion_guard_(%s, %s, \"%s\")) return false;", g.N.builder, g.N.level, kernelMethodName); g.out("boolean %s = true;", g.N.result); g.out("while (true) {"); g.out("Marker %s = enter_section_(%s, %s, _LEFT_, null);", g.N.marker, g.N.builder, g.N.level); first = true; for (String opCall : sortedOpCalls) { OperatorInfo operator = ContainerUtil.getFirstItem(findOperators(opCalls.get(opCall), OperatorType.BINARY, OperatorType.N_ARY, OperatorType.POSTFIX)); if (operator == null) continue; int priority = info.getPriority(operator.rule); int arg2Priority = operator.arg2 == null ? -1 : info.getPriority(operator.arg2); int argPriority = arg2Priority == -1 ? priority : arg2Priority - 1; String substCheck = ""; if (operator.arg1 != null) { substCheck = format(" && leftMarkerIs(%s, %s)", g.N.builder, g.getElementType(operator.arg1)); } g.out("%sif (%s < %d%s && %s) {", first ? "" : "else ", g.N.priority, priority, substCheck, opCall); first = false; String elementType = g.getElementType(operator.rule); boolean rightAssociative = ParserGeneratorUtil.getAttribute(operator.rule, KnownAttribute.RIGHT_ASSOCIATIVE); String tailCall = operator.tail == null ? null : g.generateNodeCall(operator.rule, operator.tail, getNextName(getFuncName(operator.rule), 1), ConsumeType.DEFAULT); if (operator.type == OperatorType.BINARY) { String argCall = format("%s(%s, %s, %d)", methodName, g.N.builder, g.N.level, rightAssociative ? argPriority - 1 : argPriority); g.out("%s = %s;", g.N.result, tailCall == null ? argCall : format("report_error_(%s, %s)", g.N.builder, argCall)); if (tailCall != null) g.out("%s = %s && %s;", g.N.result, tailCall, g.N.result); } else if (operator.type == OperatorType.N_ARY) { boolean checkEmpty = info.checkEmpty.contains(operator); if (checkEmpty) { g.out("int %s = current_position_(%s);", g.N.pos, g.N.builder); } g.out("while (true) {"); g.out("%s = report_error_(%s, %s(%s, %s, %d));", g.N.result, g.N.builder, methodName, g.N.builder, g.N.level, argPriority); if (tailCall != null) g.out("%s = %s && %s;", g.N.result, tailCall, g.N.result); g.out("if (!%s) break;", opCall); if (checkEmpty) { g.out("if (!empty_element_parsed_guard_(%s, \"%s\", %s)) break;", g.N.builder, operator.rule.getName(), g.N.pos); g.out("%s = current_position_(%s);", g.N.pos, g.N.builder); } g.out("}"); } else if (operator.type == OperatorType.POSTFIX) { g.out("%s = true;", g.N.result); } g.out("exit_section_(%s, %s, %s, %s, %s, true, null);", g.N.builder, g.N.level, g.N.marker, elementType, g.N.result); g.out("}"); } if (first) { g.out("// no BINARY or POSTFIX operators present"); g.out("break;"); } else { g.out("else {"); g.out("exit_section_(%s, %s, %s, null, false, false, null);", g.N.builder, g.N.level, g.N.marker); g.out("break;"); g.out("}"); } g.out("}"); g.out("return %s;", g.N.result); g.out("}"); // operators and tails Set<BnfExpression> visited = ContainerUtil.newTroveSet(); for (String opCall : sortedOpCalls) { for (OperatorInfo operator : opCalls.get(opCall)) { if (operator.type == OperatorType.ATOM) { if (Rule.isExternal(operator.rule)) continue; g.newLine(); g.generateNode(operator.rule, operator.rule.getExpression(), getFuncName(operator.rule), visited); continue; } else if (operator.type == OperatorType.PREFIX) { g.newLine(); String operatorFuncName = operator.rule.getName(); g.out("public static boolean %s(PsiBuilder %s, int %s) {", operatorFuncName, g.N.builder, g.N.level); g.out("if (!recursion_guard_(%s, %s, \"%s\")) return false;", g.N.builder, g.N.level, operatorFuncName); g.generateFirstCheck(operator.rule, frameName, false); g.out("boolean %s, %s;", g.N.result, g.N.pinned); g.out("Marker %s = enter_section_(%s, %s, _NONE_, null);", g.N.marker, g.N.builder, g.N.level); String elementType = g.getElementType(operator.rule); String tailCall = operator.tail == null ? null : g.generateNodeCall(operator.rule, operator.tail, getNextName(getFuncName(operator.rule), 1), ConsumeType.DEFAULT); g.out("%s = %s;", g.N.result, opCall); g.out("%s = %s;", g.N.pinned, g.N.result); int priority = info.getPriority(operator.rule); int arg1Priority = operator.arg1 == null ? -1 : info.getPriority(operator.arg1); int argPriority = arg1Priority == -1 ? (priority == info.nextPriority - 1 ? -1 : priority) : arg1Priority - 1; g.out("%s = %s && %s(%s, %s, %d);", g.N.result, g.N.pinned, methodName, g.N.builder, g.N.level, argPriority); if (tailCall != null) { g.out("%s = %s && report_error_(%s, %s) && %s;", g.N.result, g.N.pinned, g.N.builder, tailCall, g.N.result); } String elementTypeRef = StringUtil.isNotEmpty(elementType) ? elementType : "null"; g.out("exit_section_(%s, %s, %s, %s, %s, %s, null);", g.N.builder, g.N.level, g.N.marker, elementTypeRef, g.N.result, g.N.pinned); g.out("return %s || %s;", g.N.result, g.N.pinned); g.out("}"); } g.generateNodeChild(operator.rule, operator.operator, getFuncName(operator.rule), 0, visited); if (operator.tail != null) { g.generateNodeChild(operator.rule, operator.tail, operator.rule.getName(), 1, visited); } } } } @NotNull public static List<OperatorInfo> findOperators(Collection<OperatorInfo> list, OperatorType... types) { List<OperatorInfo> result = ContainerUtil.newSmartList(); List<OperatorType> typeList = Arrays.asList(types); for (OperatorInfo o : list) { if (ContainerUtil.find(typeList, o.type) != null) { result.add(o); } } if (result.size() > 1) { OperatorInfo info = list.iterator().next(); addWarning(info.rule.getProject(), "only first definition will be used for '" + info.operator.getText() + "': " + result); } return result; } @Nullable public static ExpressionHelper.ExpressionInfo getInfoForExpressionParsing(ExpressionHelper expressionHelper, BnfRule rule) { ExpressionHelper.ExpressionInfo expressionInfo = expressionHelper.getExpressionInfo(rule); OperatorInfo operatorInfo = expressionInfo == null ? null : expressionInfo.operatorMap.get(rule); if (expressionInfo != null && (operatorInfo == null || operatorInfo.type != OperatorType.ATOM && operatorInfo.type != OperatorType.PREFIX)) { return expressionInfo; } return null; } @Nullable public static ConsumeType fixForcedConsumeType(@NotNull ExpressionHelper expressionHelper, @NotNull BnfRule rule, @Nullable BnfExpression node, @Nullable ConsumeType defValue) { if (defValue != null) return defValue; ExpressionHelper.ExpressionInfo expressionInfo = expressionHelper.getExpressionInfo(rule); ExpressionHelper.OperatorInfo operatorInfo = expressionInfo == null ? null : expressionInfo.operatorMap.get(rule); if (operatorInfo != null) { if (node == null) { return operatorInfo.type == OperatorType.PREFIX || operatorInfo.type == OperatorType.ATOM ? CONSUME_TYPE_OVERRIDE : null; } if (PsiTreeUtil.isAncestor(operatorInfo.operator, node, false)) return CONSUME_TYPE_OVERRIDE; for (BnfExpression o : ExpressionHelper.getOriginalExpressions(operatorInfo.operator)) { if (PsiTreeUtil.isAncestor(o, node, false)) { return CONSUME_TYPE_OVERRIDE; } } } return null; } }