diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/cfg/CFASTBuilderImpl.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/cfg/CFASTBuilderImpl.java index a23692345b..323b1add4b 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/cfg/CFASTBuilderImpl.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/cfg/CFASTBuilderImpl.java @@ -159,7 +159,13 @@ private void traverse(CFASTNode parent, Node node) { node.getChildren().forEach(child -> traverse(parent, child)); addChild(parent, new CFASTNode(CFASTNodeType.END_EXEC.getValue(), convertLocation(node))); } else if (node instanceof ExecSqlWheneverNode) { - addChild(parent, new CFASTNode(CFASTNodeType.EXEC_SQL_WHENEVER.getValue(), convertLocation(node))); + ExecSqlWheneverNode wheneverNode = (ExecSqlWheneverNode) node; + SqlWhenever cfastNode = new SqlWhenever(convertLocation(node), + wheneverNode.getWheneverConditionType().name(), + wheneverNode.getWheneverType().name(), + wheneverNode.getValue()); + + addChild(parent, cfastNode); node.getChildren().forEach(child -> traverse(parent, child)); addChild(parent, new CFASTNode(CFASTNodeType.END_EXEC.getValue(), convertLocation(node))); } else if (node instanceof StopNode) { diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/model/extendedapi/SqlWhenever.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/model/extendedapi/SqlWhenever.java new file mode 100644 index 0000000000..28c87f3e9d --- /dev/null +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/model/extendedapi/SqlWhenever.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 Broadcom. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + * + */ +package org.eclipse.lsp.cobol.core.model.extendedapi; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +/** Data transport object of CF AST. GoTo type is EXEC SQL WHENEVER statement node. */ +@ToString(callSuper = true) +@Getter +@EqualsAndHashCode(callSuper = true) +public class SqlWhenever extends CFASTNode { + private final String wheneverCondition; + private final String wheneverType; + private final String value; + + public SqlWhenever(Location location, String wheneverCondition, String wheneverType, String value) { + super(CFASTNodeType.EXEC_SQL_WHENEVER.getValue(), location); + this.wheneverCondition = wheneverCondition; + this.wheneverType = wheneverType; + this.value = value; + } +} diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/sql/Db2SqlVisitor.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/sql/Db2SqlVisitor.java index b7d73a1ca5..17636d2d52 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/sql/Db2SqlVisitor.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/sql/Db2SqlVisitor.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -36,6 +37,7 @@ import org.antlr.v4.runtime.tree.RuleNode; import org.antlr.v4.runtime.tree.TerminalNode; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; import org.eclipse.lsp.cobol.common.copybook.CopybookService; import org.eclipse.lsp.cobol.common.dialects.CobolDialect; import org.eclipse.lsp.cobol.common.dialects.DialectProcessingContext; @@ -284,7 +286,13 @@ public List visitProcedureDivisionRules(Db2SqlParser.ProcedureDivisionRule @Override public List visitDbs_whenever(Db2SqlParser.Dbs_wheneverContext ctx) { - return addTreeNode(ctx, ExecSqlWheneverNode::new); + ExecSqlWheneverNode.WheneverConditionType conditionType = getConditionType(ctx); + Pair result = getWheneverType(ctx); + + return addTreeNode(ctx, location -> new ExecSqlWheneverNode(location, + conditionType, + result.getKey(), + result.getValue())); } @Override @@ -425,4 +433,35 @@ private List addVariableUsageNodes(ParserRuleContext ctx) { String finalName = name; return addTreeNode(ctx, locality -> new VariableUsageNode(finalName, locality)); } + } + + private ExecSqlWheneverNode.WheneverConditionType getConditionType(Db2SqlParser.Dbs_wheneverContext ctx) { + ParserRuleContext ruleContext = ((ParserRuleContext) ctx); + ExecSqlWheneverNode.WheneverConditionType conditionType = ExecSqlWheneverNode.WheneverConditionType.NOT_FOUND; + + if (ruleContext.getChildCount() >= 3) { + ParseTree pt1 = ruleContext.getChild(1); + if (Objects.equals(pt1.getText(), "SQLERROR")) { + conditionType = ExecSqlWheneverNode.WheneverConditionType.SQLERROR; + } else if (Objects.equals(pt1.getText(), "SQLWARNING")) { + conditionType = ExecSqlWheneverNode.WheneverConditionType.SQLWARNING; + } + } + return conditionType; + } + + private Pair getWheneverType(Db2SqlParser.Dbs_wheneverContext ctx) { + ParserRuleContext ruleContext = ((ParserRuleContext) ctx); + Pair result = Pair.of(ExecSqlWheneverNode.WheneverType.CONTINUE, null); + + if (ruleContext.getChildCount() > 3) { + ParseTree pt1 = ruleContext.getChild(2); + if (Objects.equals(pt1.getText(), "DO")) { + result = Pair.of(ExecSqlWheneverNode.WheneverType.DO, ruleContext.getChild(3).getText()); + } else if (Objects.equals(pt1.getText(), "GO TO") || Objects.equals(pt1.getText(), "GOTO")) { + result = Pair.of(ExecSqlWheneverNode.WheneverType.GOTO, ruleContext.getChild(3).getText()); + } + } + return result; + } } diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/sql/node/ExecSqlWheneverNode.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/sql/node/ExecSqlWheneverNode.java index 22ab85bd52..d9380af03a 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/sql/node/ExecSqlWheneverNode.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/sql/node/ExecSqlWheneverNode.java @@ -16,6 +16,7 @@ package org.eclipse.lsp.cobol.implicitDialects.sql.node; import lombok.EqualsAndHashCode; +import lombok.Getter; import lombok.ToString; import org.eclipse.lsp.cobol.common.model.Locality; import org.eclipse.lsp.cobol.common.model.NodeType; @@ -25,9 +26,34 @@ /** EXEC CICS block node */ @ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) +@Getter public class ExecSqlWheneverNode extends Node { - public ExecSqlWheneverNode(Locality location) { + private final WheneverConditionType wheneverConditionType; + private final WheneverType wheneverType; + private final String value; + + /** Represents a condition clause type of the EXEC SQL WHENEVER statement **/ + public enum WheneverConditionType { + NOT_FOUND, + SQLERROR, + SQLWARNING + } + + /** Represents a verb clause type of the EXEC SQL WHENEVER statement **/ + public enum WheneverType { + CONTINUE, + GOTO, + DO + } + + public ExecSqlWheneverNode(Locality location, + WheneverConditionType wheneverConditionType, + WheneverType wheneverType, + String value) { super(location, NodeType.STATEMENT, Db2SqlDialect.DIALECT_NAME); + this.wheneverConditionType = wheneverConditionType; + this.wheneverType = wheneverType; + this.value = value; } } diff --git a/server/engine/src/test/resources/cfast/case_execSqlWhenever.result.json b/server/engine/src/test/resources/cfast/case_execSqlWhenever.result.json index 66ec6e9cda..6c4cacc014 100644 --- a/server/engine/src/test/resources/cfast/case_execSqlWhenever.result.json +++ b/server/engine/src/test/resources/cfast/case_execSqlWhenever.result.json @@ -15,6 +15,9 @@ }, "children": [ { + "wheneverCondition": "SQLERROR", + "wheneverType": "GOTO", + "value": "HANDLER", "type": "execwhenever", "location": { "uri": "fake/path",