From 63348ed1b827376602786b7d283cf94c302acc45 Mon Sep 17 00:00:00 2001 From: alkoleft Date: Fri, 11 Nov 2022 23:52:35 +0300 Subject: [PATCH] #2934 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Тесты Исправлена ошибка обработки выражения в скобках --- ...rocessorExpressionTreeBuildingVisitor.java | 52 ++++++-- .../ExpressionParseTreeRewriterTest.java | 125 ++++++++++++++++++ 2 files changed, 165 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/expressiontree/PreprocessorExpressionTreeBuildingVisitor.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/expressiontree/PreprocessorExpressionTreeBuildingVisitor.java index e1ba4715d6c..b65c274d414 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/expressiontree/PreprocessorExpressionTreeBuildingVisitor.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/expressiontree/PreprocessorExpressionTreeBuildingVisitor.java @@ -10,10 +10,8 @@ class PreprocessorExpressionTreeBuildingVisitor extends BSLParserBaseVisitor { private BslExpression resultExpression; - private int recursionLevel = -1; - private final Deque operands = new ArrayDeque<>(); - private final Deque operators = new ArrayDeque<>(); + private final Deque states = new ArrayDeque<>(); BslExpression getExpressionTree() { return resultExpression; @@ -21,31 +19,37 @@ BslExpression getExpressionTree() { @Override public ParseTree visitPreproc_expression(BSLParser.Preproc_expressionContext ctx) { - return super.visitPreproc_expression(ctx); + super.visitPreproc_expression(ctx); + + return ctx; } @Override public ParseTree visitPreproc_logicalExpression(BSLParser.Preproc_logicalExpressionContext ctx) { - var nestingCount = operators.size(); - recursionLevel++; - var addToOperands = recursionLevel > 0; + + boolean isRoot = states.isEmpty(); + var currentState = new State(); + states.push(currentState); super.visitPreproc_logicalExpression(ctx); - while (nestingCount < operators.size()) { + while (!currentState.operators.isEmpty()) { buildOperation(ctx); } - if (!addToOperands) { - resultExpression = operands.pop(); - } + states.remove(); - recursionLevel--; + if (isRoot) { + resultExpression = currentState.operands.pop(); + } else { + getOperands().push(currentState.operands.pop()); + } return ctx; } @Override public ParseTree visitPreproc_logicalOperand(BSLParser.Preproc_logicalOperandContext ctx) { + var operands = getOperands(); if (ctx.preproc_symbol() != null) { operands.push(new PreprocessorSymbolNode(ctx.preproc_symbol())); @@ -54,6 +58,7 @@ public ParseTree visitPreproc_logicalOperand(BSLParser.Preproc_logicalOperandCon } if (ctx.PREPROC_NOT_KEYWORD() != null) { + var operators = getOperators(); operators.push(BslOperator.NOT); buildOperation(ctx); } @@ -72,6 +77,7 @@ public ParseTree visitPreproc_boolOperation(BSLParser.Preproc_boolOperationConte } private void processOperation(BslOperator operator, ParseTree ctx) { + var operators = getOperators(); if (operators.isEmpty()) { operators.push(operator); return; @@ -86,10 +92,12 @@ private void processOperation(BslOperator operator, ParseTree ctx) { } private void buildOperation(ParseTree ctx) { + var operators = getOperators(); if (operators.isEmpty()) { return; } + var operands = getOperands(); var operator = operators.pop(); if (operator == BslOperator.NOT) { var operand = operands.pop(); @@ -102,4 +110,24 @@ private void buildOperation(ParseTree ctx) { operands.push(binaryOp); } } + + private Deque getOperands() { + if (states.isEmpty()) { + throw new IllegalStateException(); + } + return states.peek().operands; + } + + private Deque getOperators() { + if (states.isEmpty()) { + throw new IllegalStateException(); + } + return states.peek().operators; + } + + private static class State { + private final Deque operands = new ArrayDeque<>(); + private final Deque operators = new ArrayDeque<>(); + + } } diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/utils/ExpressionParseTreeRewriterTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/utils/ExpressionParseTreeRewriterTest.java index 3893037cf21..7cb99b8a368 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/utils/ExpressionParseTreeRewriterTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/utils/ExpressionParseTreeRewriterTest.java @@ -21,6 +21,7 @@ */ package com.github._1c_syntax.bsl.languageserver.utils; +import com.github._1c_syntax.bsl.languageserver.cfg.PreprocessorConstraints; import com.github._1c_syntax.bsl.languageserver.util.TestUtils; import com.github._1c_syntax.bsl.languageserver.utils.expressiontree.BinaryOperationNode; import com.github._1c_syntax.bsl.languageserver.utils.expressiontree.BslExpression; @@ -30,12 +31,15 @@ import com.github._1c_syntax.bsl.languageserver.utils.expressiontree.ExpressionNodeType; import com.github._1c_syntax.bsl.languageserver.utils.expressiontree.ExpressionParseTreeRewriter; import com.github._1c_syntax.bsl.languageserver.utils.expressiontree.MethodCallNode; +import com.github._1c_syntax.bsl.languageserver.utils.expressiontree.PreprocessorSymbolNode; import com.github._1c_syntax.bsl.languageserver.utils.expressiontree.SkippedCallArgumentNode; import com.github._1c_syntax.bsl.languageserver.utils.expressiontree.UnaryOperationNode; import com.github._1c_syntax.bsl.parser.BSLParser; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import java.util.Map; + import static org.assertj.core.api.Assertions.as; import static org.assertj.core.api.Assertions.assertThat; @@ -317,6 +321,127 @@ void realLifeHardExpression() { assertThat(binary.getRight().cast().getOperator()).isEqualTo(BslOperator.EQUAL); } + @Test + void preprocessorUno() { + var variants = Map.ofEntries( + Map.entry("Клиент", PreprocessorConstraints.CLIENT), + Map.entry("Client", PreprocessorConstraints.CLIENT), + Map.entry("НаКлиенте", PreprocessorConstraints.CLIENT), + Map.entry("AtClient", PreprocessorConstraints.CLIENT), + Map.entry("НаСервере", PreprocessorConstraints.SERVER), + Map.entry("AtServer", PreprocessorConstraints.SERVER), + Map.entry("Сервер", PreprocessorConstraints.SERVER), + Map.entry("Server", PreprocessorConstraints.SERVER), + Map.entry("ТонкийКлиент", PreprocessorConstraints.THIN_CLIENT), + Map.entry("ThinClient", PreprocessorConstraints.THIN_CLIENT), + Map.entry("ВебКлиент", PreprocessorConstraints.WEB_CLIENT), + Map.entry("WebClient", PreprocessorConstraints.WEB_CLIENT), + Map.entry("МобильныйАвтономныйСервер", PreprocessorConstraints.MOBILE_STANDALONE_SERVER), + Map.entry("MobileStandaloneServer", PreprocessorConstraints.MOBILE_STANDALONE_SERVER), + Map.entry("МобильноеПриложениеКлиент", PreprocessorConstraints.MOBILE_APP_CLIENT), + Map.entry("MobileAppClient", PreprocessorConstraints.MOBILE_APP_CLIENT), + Map.entry("МобильноеПриложениеСервер", PreprocessorConstraints.MOBILE_APP_SERVER), + Map.entry("MobileAppServer", PreprocessorConstraints.MOBILE_APP_SERVER), + Map.entry("МобильныйКлиент", PreprocessorConstraints.MOBILE_CLIENT), + Map.entry("MobileClient", PreprocessorConstraints.MOBILE_CLIENT), + Map.entry("ТолстыйКлиентОбычноеПриложение", PreprocessorConstraints.ORDINARY_THICK_CLIENT), + Map.entry("ThickClientOrdinaryApplication", PreprocessorConstraints.ORDINARY_THICK_CLIENT), + Map.entry("ТолстыйКлиентУправляемоеПриложение", PreprocessorConstraints.MANAGED_THICK_CLIENT), + Map.entry("ThickClientManagedApplication", PreprocessorConstraints.MANAGED_THICK_CLIENT), + Map.entry("ВнешнееСоединение", PreprocessorConstraints.EXTERNAL_CONNECTION), + Map.entry("ExternalConnection", PreprocessorConstraints.EXTERNAL_CONNECTION)); + + for (var variant : variants.entrySet()) { + var expression = getPreprocessorExpressionTree(variant.getKey()); + assertThat(expression).isInstanceOf(PreprocessorSymbolNode.class); + assertThat(((PreprocessorSymbolNode) expression).getSymbol()).isEqualTo(variant.getValue()); + } + } + + @Test + void preprocessorNot() { + var variants = Map.of( + "Not Клиент", PreprocessorConstraints.CLIENT, + "Не Server", PreprocessorConstraints.SERVER + ); + + for (var variant : variants.entrySet()) { + var expression = getPreprocessorExpressionTree(variant.getKey()); + assertThat(expression).isInstanceOf(UnaryOperationNode.class); + var operation = (UnaryOperationNode) expression; + assertThat(operation.getOperator()).isEqualTo(BslOperator.NOT); + assertThat(operation.getOperand()).isInstanceOf(PreprocessorSymbolNode.class); + assertThat(((PreprocessorSymbolNode) operation.getOperand()).getSymbol()).isEqualTo(variant.getValue()); + } + } + + @Test + void preprocessorAND() { + var expression = getPreprocessorExpressionTree("Сервер И Клиент"); + assertThat(expression).isInstanceOf(BinaryOperationNode.class); + var operation = (BinaryOperationNode) expression; + assertThat(operation.getOperator()).isEqualTo(BslOperator.AND); + assertThat(operation.getLeft()) + .isInstanceOf(PreprocessorSymbolNode.class) + .extracting("symbol").isEqualTo(PreprocessorConstraints.SERVER) + ; + assertThat(operation.getRight()) + .isInstanceOf(PreprocessorSymbolNode.class) + .extracting("symbol").isEqualTo(PreprocessorConstraints.CLIENT) + ; + expression = getPreprocessorExpressionTree("НЕ Сервер И Клиент"); + assertThat(expression) + .extracting("left").isInstanceOf(UnaryOperationNode.class) + .extracting("operand") + .isInstanceOf(PreprocessorSymbolNode.class) + .extracting("symbol").isEqualTo(PreprocessorConstraints.SERVER) + ; + expression = getPreprocessorExpressionTree("Клиент AND Server AND MobileAppClient"); + operation = (BinaryOperationNode) expression; + assertThat(operation.getLeft()).isInstanceOf(PreprocessorSymbolNode.class) + .extracting("symbol").isEqualTo(PreprocessorConstraints.CLIENT); + assertThat(operation.getRight()).isInstanceOf(BinaryOperationNode.class); + } + + @Test + void preprocessorOR() { + var expression = getPreprocessorExpressionTree("Сервер ИЛИ Клиент"); + assertThat(expression).isInstanceOf(BinaryOperationNode.class); + var operation = (BinaryOperationNode) expression; + assertThat(operation.getOperator()).isEqualTo(BslOperator.OR); + assertThat(operation.getLeft()) + .isInstanceOf(PreprocessorSymbolNode.class) + .extracting("symbol").isEqualTo(PreprocessorConstraints.SERVER) + ; + expression = getPreprocessorExpressionTree("Клиент OR Server OR MobileAppClient"); + operation = (BinaryOperationNode) expression; + assertThat(operation.getLeft()).isInstanceOf(PreprocessorSymbolNode.class) + .extracting("symbol").isEqualTo(PreprocessorConstraints.CLIENT); + assertThat(operation.getRight()).isInstanceOf(BinaryOperationNode.class); + } + + @Test + void preprocessorComplex() { + var expression = getPreprocessorExpressionTree("Client AND Not MobileClient OR Server И (ExternalConnection ИЛИ Клиент)"); + var operation = (BinaryOperationNode) expression; + assertThat(operation.getOperator()).isEqualTo(BslOperator.OR); + assertThat(operation.getLeft()) + .extracting("left.symbol", "operator", "right.operator", "right.operand.symbol") + .containsExactly(PreprocessorConstraints.CLIENT, BslOperator.AND, BslOperator.NOT, PreprocessorConstraints.MOBILE_CLIENT) + ; + assertThat(operation.getRight()) + .extracting("left.symbol", "operator", "right.left.symbol", "right.operator", "right.right.symbol") + .containsExactly(PreprocessorConstraints.SERVER, BslOperator.AND, PreprocessorConstraints.EXTERNAL_CONNECTION, BslOperator.OR, PreprocessorConstraints.CLIENT) + ; + } + + BslExpression getPreprocessorExpressionTree(String code) { + var preprocessorPredicate = String.format("#Если %s Тогда\n#КонецЕсли", code); + var dContext = TestUtils.getDocumentContext(preprocessorPredicate); + var expression = dContext.getAst().preprocessor(0).preproc_if().preproc_expression(); + return ExpressionParseTreeRewriter.buildExpressionTree(expression); + } + BslExpression getExpressionTree(String code) { var expression = parse(code); return ExpressionParseTreeRewriter.buildExpressionTree(expression);