Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Символьное вычисление условий препроцессора #2935

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
*/
package com.github._1c_syntax.bsl.languageserver.cfg;

import com.google.common.collect.Sets;

import java.util.Set;

public enum PreprocessorConstraints {
SERVER,
CLIENT,
Expand All @@ -30,9 +34,28 @@ public enum PreprocessorConstraints {
WEB_CLIENT,
MOBILE_CLIENT,
MOBILE_APP_CLIENT,
MOBILE_STANDALONE_SERVER,
MOBILE_APP_SERVER,
EXTERNAL_CONNECTION,

NON_STANDARD
NON_STANDARD;

public static final Set<PreprocessorConstraints> CLIENT_CONSTRAINTS = Sets.immutableEnumSet(
ORDINARY_THICK_CLIENT,
MANAGED_THICK_CLIENT,
MOBILE_CLIENT,
THIN_CLIENT,
WEB_CLIENT);

public static final Set<PreprocessorConstraints> DEFAULT_CONSTRAINTS = Sets.immutableEnumSet(
SERVER,
THIN_CLIENT,
MANAGED_THICK_CLIENT,
ORDINARY_THICK_CLIENT,
WEB_CLIENT,
MOBILE_CLIENT,
MOBILE_APP_CLIENT,
MOBILE_STANDALONE_SERVER,
MOBILE_APP_SERVER,
EXTERNAL_CONNECTION);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package com.github._1c_syntax.bsl.languageserver.utils.bsl;

import com.github._1c_syntax.bsl.languageserver.cfg.PreprocessorConstraints;
import com.github._1c_syntax.bsl.languageserver.utils.expressiontree.BinaryOperationNode;
import com.github._1c_syntax.bsl.languageserver.utils.expressiontree.BslExpression;
import com.github._1c_syntax.bsl.languageserver.utils.expressiontree.BslOperator;
import com.github._1c_syntax.bsl.languageserver.utils.expressiontree.PreprocessorSymbolNode;
import com.github._1c_syntax.bsl.languageserver.utils.expressiontree.UnaryOperationNode;
import com.github._1c_syntax.bsl.parser.BSLParser;
import lombok.experimental.UtilityClass;
import org.antlr.v4.runtime.tree.TerminalNode;

import java.util.EnumSet;

/**
* Набор методов для работы с препроцессором языка BSL
*/
@UtilityClass
public class Preprocessor {

/**
* @param ctx контекст синтаксического дерева
* @return ограничение препроцессора
*/
public static PreprocessorConstraints getPreprocessorConstraint(BSLParser.Preproc_symbolContext ctx) {
PreprocessorConstraints symbol;

switch (((TerminalNode) ctx.getChild(0)).getSymbol().getType()) {
Fixed Show fixed Hide fixed
case BSLParser.PREPROC_ATSERVER_SYMBOL:
case BSLParser.PREPROC_SERVER_SYMBOL:
symbol = PreprocessorConstraints.SERVER;
break;
case BSLParser.PREPROC_CLIENT_SYMBOL:
case BSLParser.PREPROC_ATCLIENT_SYMBOL:
symbol = PreprocessorConstraints.CLIENT;
break;
case BSLParser.PREPROC_THINCLIENT_SYMBOL:
symbol = PreprocessorConstraints.THIN_CLIENT;
break;
case BSLParser.PREPROC_MOBILECLIENT_SYMBOL:
symbol = PreprocessorConstraints.MOBILE_CLIENT;
break;
case BSLParser.PREPROC_WEBCLIENT_SYMBOL:
symbol = PreprocessorConstraints.WEB_CLIENT;
break;
case BSLParser.PREPROC_EXTERNALCONNECTION_SYMBOL:
symbol = PreprocessorConstraints.EXTERNAL_CONNECTION;
break;
case BSLParser.PREPROC_THICKCLIENTMANAGEDAPPLICATION_SYMBOL:
symbol = PreprocessorConstraints.MANAGED_THICK_CLIENT;
break;
case BSLParser.PREPROC_THICKCLIENTORDINARYAPPLICATION_SYMBOL:
symbol = PreprocessorConstraints.ORDINARY_THICK_CLIENT;
break;
case BSLParser.PREPROC_MOBILE_STANDALONE_SERVER:
symbol = PreprocessorConstraints.MOBILE_STANDALONE_SERVER;
break;
case BSLParser.PREPROC_MOBILEAPPCLIENT_SYMBOL:
symbol = PreprocessorConstraints.MOBILE_APP_CLIENT;
break;
case BSLParser.PREPROC_MOBILEAPPSERVER_SYMBOL:
symbol = PreprocessorConstraints.MOBILE_APP_SERVER;
break;
default:
symbol = PreprocessorConstraints.NON_STANDARD;
break;
}
return symbol;
}

/**
* Вычисляет результат условия препроцессора
*
* @param expression условие препроцессора
* @return результирующий набор контекстов
*/
public static EnumSet<PreprocessorConstraints> calculatePreprocessorConstraints(BslExpression expression) {

if (expression instanceof PreprocessorSymbolNode) {

return getConstraintSet(((PreprocessorSymbolNode) expression).getSymbol());

} else if (expression instanceof UnaryOperationNode && ((UnaryOperationNode) expression).getOperator() == BslOperator.NOT) {

var subset = calculatePreprocessorConstraints(((UnaryOperationNode) expression).getOperand());
return getInventorySet(subset);

} else if (expression instanceof BinaryOperationNode) {

var operation = (BinaryOperationNode) expression;

if (operation.getOperator() == BslOperator.AND) {

return retainSets(
calculatePreprocessorConstraints(operation.getLeft()),
calculatePreprocessorConstraints(operation.getRight()));

} else if (operation.getOperator() == BslOperator.OR) {

return joinSets(
calculatePreprocessorConstraints(operation.getLeft()),
calculatePreprocessorConstraints(operation.getRight()));

}
}

throw new IllegalStateException();
}

private static EnumSet<PreprocessorConstraints> getConstraintSet(PreprocessorConstraints constraint) {
if (constraint == PreprocessorConstraints.CLIENT) {
return EnumSet.copyOf(PreprocessorConstraints.CLIENT_CONSTRAINTS);
} else {
return EnumSet.of(constraint);
}
}

private static EnumSet<PreprocessorConstraints> getInventorySet(EnumSet<PreprocessorConstraints> set) {
var resultSet = EnumSet.copyOf(PreprocessorConstraints.DEFAULT_CONSTRAINTS);
resultSet.removeAll(set);
return resultSet;
}

private static EnumSet<PreprocessorConstraints> joinSets(EnumSet<PreprocessorConstraints> firstSet, EnumSet<PreprocessorConstraints> secondSet) {
var resultSet = firstSet.clone();
resultSet.addAll(secondSet);
return resultSet;
}

private static EnumSet<PreprocessorConstraints> retainSets(EnumSet<PreprocessorConstraints> firstSet, EnumSet<PreprocessorConstraints> secondSet) {
var resultSet = firstSet.clone();
resultSet.retainAll(secondSet);
return resultSet;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,14 @@ public static BslExpression buildExpressionTree(BSLParser.ExpressionContext expr
return visitor.getExpressionTree();
}

/**
* Строит дерево выражений для условия препроцессора
* @param expression ast дерево выражения
* @return результирующее выражение в виде дерева вычисления операций
*/
public static BslExpression buildExpressionTree(BSLParser.Preproc_expressionContext expression) {
var visitor = new PreprocessorExpressionTreeBuildingVisitor();
visitor.visitPreproc_expression(expression);
return visitor.getExpressionTree();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package com.github._1c_syntax.bsl.languageserver.utils.expressiontree;

import com.github._1c_syntax.bsl.parser.BSLParser;
import com.github._1c_syntax.bsl.parser.BSLParserBaseVisitor;
import org.antlr.v4.runtime.tree.ParseTree;

import java.util.ArrayDeque;
import java.util.Deque;

class PreprocessorExpressionTreeBuildingVisitor extends BSLParserBaseVisitor<ParseTree> {

private BslExpression resultExpression;

private final Deque<State> states = new ArrayDeque<>();

BslExpression getExpressionTree() {
return resultExpression;
}

@Override
public ParseTree visitPreproc_expression(BSLParser.Preproc_expressionContext ctx) {

boolean isRoot = states.isEmpty();
var currentState = new State();
states.push(currentState);

super.visitPreproc_expression(ctx);

if (ctx.PREPROC_NOT_KEYWORD() != null) {
pushNot(ctx);
}
states.remove();

if (isRoot) {
resultExpression = currentState.operands.pop();
} else {
getOperands().push(currentState.operands.pop());
}

return ctx;
}

@Override
public ParseTree visitPreproc_logicalExpression(BSLParser.Preproc_logicalExpressionContext ctx) {

var currentState = new State();
states.push(currentState);

super.visitPreproc_logicalExpression(ctx);

while (!currentState.operators.isEmpty()) {
buildOperation(ctx);
}

states.remove();
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()));
} else {
super.visitPreproc_logicalOperand(ctx);
}

if (ctx.PREPROC_NOT_KEYWORD() != null) {
pushNot(ctx);
}

return ctx;
}

@Override
public ParseTree visitPreproc_boolOperation(BSLParser.Preproc_boolOperationContext ctx) {
if (ctx.PREPROC_AND_KEYWORD() != null) {
processOperation(BslOperator.AND, ctx);
} else if (ctx.PREPROC_OR_KEYWORD() != null) {
processOperation(BslOperator.OR, ctx);
}
return ctx;
}

private void processOperation(BslOperator operator, ParseTree ctx) {
var operators = getOperators();
if (operators.isEmpty()) {
operators.push(operator);
return;
}

var lastSeenOperator = operators.peek();
if (lastSeenOperator.getPriority() > operator.getPriority()) {
buildOperation(ctx);
}

operators.push(operator);
}

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();
var operation = UnaryOperationNode.create(operator, operand, ctx);
operands.push(operation);
} else {
var right = operands.pop();
var left = operands.pop();
var binaryOp = BinaryOperationNode.create(operator, left, right, ctx);
operands.push(binaryOp);
}
}

private Deque<BslExpression> getOperands() {
if (states.isEmpty()) {
throw new IllegalStateException();
}
return states.peek().operands;
}

private Deque<BslOperator> getOperators() {
if (states.isEmpty()) {
throw new IllegalStateException();
}
return states.peek().operators;
}

private void pushNot(ParseTree ctx) {
var operators = getOperators();
operators.push(BslOperator.NOT);
buildOperation(ctx);
}

private static class State {
private final Deque<BslExpression> operands = new ArrayDeque<>();
private final Deque<BslOperator> operators = new ArrayDeque<>();

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.github._1c_syntax.bsl.languageserver.utils.expressiontree;

import com.github._1c_syntax.bsl.languageserver.cfg.PreprocessorConstraints;
import com.github._1c_syntax.bsl.languageserver.utils.bsl.Preprocessor;
import com.github._1c_syntax.bsl.parser.BSLParser;
import lombok.Getter;
import lombok.ToString;

@ToString
public class PreprocessorSymbolNode extends BslExpression {

@Getter
private final PreprocessorConstraints symbol;

PreprocessorSymbolNode(BSLParser.Preproc_symbolContext ctx) {
super(ExpressionNodeType.LITERAL);
symbol = Preprocessor.getPreprocessorConstraint(ctx);
}
}
Loading