Skip to content

Commit 39ead12

Browse files
committed
feat: Support for REDEFINES clause eclipse-che4z#867
Signed-off-by: Leonid Baranov <[email protected]>
1 parent a9f5b3d commit 39ead12

13 files changed

+457
-42
lines changed

server/src/main/java/org/eclipse/lsp/cobol/core/model/tree/NodeType.java

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public enum NodeType {
2222
PROGRAM,
2323
SECTION,
2424
VARIABLE_DEFINITION,
25+
VARIABLE_REDEFINITION,
2526
VARIABLE_USAGE,
2627
ANTLR_VARIABLE_DEFINITION,
2728
PROGRAM_ID,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (c) 2021 Broadcom.
3+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
4+
*
5+
* This program and the accompanying materials are made
6+
* available under the terms of the Eclipse Public License 2.0
7+
* which is available at https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* Broadcom, Inc. - initial API and implementation
13+
*
14+
*/
15+
package org.eclipse.lsp.cobol.core.model.tree;
16+
17+
import org.eclipse.lsp.cobol.core.CobolParser;
18+
import org.eclipse.lsp.cobol.core.model.Locality;
19+
20+
import java.util.Optional;
21+
22+
/**
23+
* The node represents a variable redefinition clause
24+
*/
25+
public class VariableRedefinitionNode extends Node {
26+
27+
private final CobolParser.DataRedefinesClauseContext redefinesClauseContext;
28+
29+
public VariableRedefinitionNode(Locality locality, CobolParser.DataRedefinesClauseContext redefinesClauseContext) {
30+
super(locality, NodeType.VARIABLE_REDEFINITION);
31+
this.redefinesClauseContext = redefinesClauseContext;
32+
}
33+
34+
@Override
35+
public void process() {
36+
getNearestParentByType(NodeType.PROGRAM)
37+
.map(ProgramNode.class::cast)
38+
.map(ProgramNode::getVariableDefinitionDelegate)
39+
.ifPresent(variablesDelegate -> {
40+
Optional.ofNullable(redefinesClauseContext).ifPresent(variablesDelegate::handleRedefine);
41+
});
42+
}
43+
}

server/src/main/java/org/eclipse/lsp/cobol/core/visitor/CobolVisitor.java

+25-35
Original file line numberDiff line numberDiff line change
@@ -478,14 +478,10 @@ public List<Node> visitParagraphName(ParagraphNameContext ctx) {
478478
@Override
479479
public List<Node> visitDataDescriptionEntryFormat1(DataDescriptionEntryFormat1Context ctx) {
480480
variablesDelegate.defineVariable(ctx);
481-
String name =
482-
ofNullable(ctx.entryName())
483-
.map(EntryNameContext::dataName1)
484-
.map(RuleContext::getText)
485-
.orElse(FILLER_NAME);
486-
String levelNumber = ctx.LEVEL_NUMBER().getText();
487-
int level = Integer.parseInt(levelNumber);
488-
outlineTreeBuilder.addVariable(level, name, getDataDescriptionNodeType(ctx), ctx);
481+
String name = VisitorHelper.getName(ctx.entryName());
482+
NodeType nodeType = getDataDescriptionNodeType(ctx);
483+
int level = VisitorHelper.getLevel(ctx.LEVEL_NUMBER());
484+
outlineTreeBuilder.addVariable(level, name, nodeType, ctx);
489485
Node node = new AntlrVariableDefinitionNode(ctx);
490486
visitChildren(ctx).forEach(node::addChild);
491487
return ImmutableList.of(node);
@@ -517,25 +513,34 @@ public List<Node> visitEnvironmentSwitchNameClause(EnvironmentSwitchNameClauseCo
517513
@Override
518514
public List<Node> visitDataDescriptionEntryFormat2(DataDescriptionEntryFormat2Context ctx) {
519515
variablesDelegate.defineVariable(ctx);
520-
String name =
521-
ofNullable(ctx.entryName())
522-
.map(EntryNameContext::dataName1)
523-
.map(RuleContext::getText)
524-
.orElse(FILLER_NAME);
516+
String name = VisitorHelper.getName(ctx.entryName());
525517
outlineTreeBuilder.addVariable(LEVEL_66, name, NodeType.FIELD_66, ctx);
526518
Node node = new AntlrVariableDefinitionNode(ctx);
527519
visitChildren(ctx).forEach(node::addChild);
528520
return ImmutableList.of(node);
529521
}
530522

523+
@Override
524+
public List<Node> visitDataRedefinesClause(DataRedefinesClauseContext ctx) {
525+
String name = VisitorHelper.getName(ctx.dataName());
526+
List<Node> result = new ArrayList<>();
527+
528+
getLocality(ctx.dataName().getStart())
529+
.ifPresent(locality -> {
530+
Node node = new VariableRedefinitionNode(locality, ctx);
531+
visitChildren(ctx).forEach(node::addChild);
532+
533+
variableUsageDelegate.handleDataName(name, locality);
534+
node.addChild(new VariableUsageNode(name, locality));
535+
result.add(node);
536+
});
537+
return result;
538+
}
539+
531540
@Override
532541
public List<Node> visitDataDescriptionEntryFormat3(DataDescriptionEntryFormat3Context ctx) {
533542
variablesDelegate.defineVariable(ctx);
534-
String name =
535-
ofNullable(ctx.entryName())
536-
.map(EntryNameContext::dataName1)
537-
.map(RuleContext::getText)
538-
.orElse(FILLER_NAME);
543+
String name = VisitorHelper.getName(ctx.entryName());
539544
outlineTreeBuilder.addVariable(LEVEL_88, name, NodeType.FIELD_88, ctx);
540545
Node node = new AntlrVariableDefinitionNode(ctx);
541546
visitChildren(ctx).forEach(node::addChild);
@@ -546,12 +551,7 @@ public List<Node> visitDataDescriptionEntryFormat3(DataDescriptionEntryFormat3Co
546551
public List<Node> visitDataDescriptionEntryFormat1Level77(
547552
DataDescriptionEntryFormat1Level77Context ctx) {
548553
variablesDelegate.defineVariable(ctx);
549-
String name =
550-
ofNullable(ctx.entryName())
551-
.map(EntryNameContext::dataName1)
552-
.map(RuleContext::getText)
553-
.orElse(FILLER_NAME);
554-
554+
String name = VisitorHelper.getName(ctx.entryName());
555555
outlineTreeBuilder.addVariable(LEVEL_77, name, NodeType.FIELD, ctx);
556556
Node node = new AntlrVariableDefinitionNode(ctx);
557557
visitChildren(ctx).forEach(node::addChild);
@@ -681,7 +681,6 @@ public List<Node> visitIfStatement(IfStatementContext ctx) {
681681
return addTreeNode(ctx, locality -> new IfNode(locality, ctx));
682682
}
683683

684-
685684
private void throwException(String wrongToken, @NonNull Locality locality, String message) {
686685
SyntaxError error =
687686
SyntaxError.syntaxError()
@@ -700,21 +699,12 @@ private Optional<Locality> getLocality(Token childToken) {
700699
return ofNullable(positionMapping.get(childToken));
701700
}
702701

703-
private Locality getIntervalPosition(Locality start, Locality stop) {
704-
return Locality.builder()
705-
.uri(start.getUri())
706-
.range(new Range(start.getRange().getStart(), stop.getRange().getEnd()))
707-
.recognizer(CobolVisitor.class)
708-
.copybookId(start.getCopybookId())
709-
.build();
710-
}
711-
712702
private void reportSubroutineNotDefined(String name, Locality locality) {
713703
SyntaxError error =
714704
SyntaxError.syntaxError()
715705
.suggestion(messageService.getMessage("CobolVisitor.subroutineNotFound", name))
716706
.severity(ErrorSeverity.INFO)
717-
.locality(getIntervalPosition(locality, locality))
707+
.locality(VisitorHelper.getIntervalPosition(locality, locality))
718708
.build();
719709
LOG.debug("Syntax error by CobolVisitor#reportSubroutineNotDefined: {}", error);
720710
errors.add(error);

server/src/main/java/org/eclipse/lsp/cobol/core/visitor/VariableDefinitionDelegate.java

+60-4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515
package org.eclipse.lsp.cobol.core.visitor;
1616

17+
import lombok.*;
1718
import org.eclipse.lsp.cobol.core.CobolParser.*;
1819
import org.eclipse.lsp.cobol.core.messages.MessageService;
1920
import org.eclipse.lsp.cobol.core.model.ErrorSeverity;
@@ -23,10 +24,6 @@
2324
import org.eclipse.lsp.cobol.core.model.variables.*;
2425
import org.eclipse.lsp.cobol.core.semantics.outline.OutlineNodeNames;
2526
import com.google.common.collect.ImmutableList;
26-
import lombok.Builder;
27-
import lombok.Data;
28-
import lombok.NonNull;
29-
import lombok.RequiredArgsConstructor;
3027
import lombok.extern.slf4j.Slf4j;
3128
import org.antlr.v4.runtime.ParserRuleContext;
3229
import org.antlr.v4.runtime.RuleContext;
@@ -83,6 +80,8 @@ public class VariableDefinitionDelegate {
8380
private final Map<Token, Locality> positions;
8481
private final MessageService messages;
8582

83+
private final Map<String, String> redefinedVariables = new HashMap<>();
84+
8685
/**
8786
* Create and accumulate a variable of level 01-49 out of the given context. Add errors if the
8887
* variable definition contains is invalid
@@ -115,6 +114,7 @@ public void defineVariable(@NonNull DataDescriptionEntryFormat1Context ctx) {
115114
.occursClauses(ctx.dataOccursClause())
116115
.valueClauses(ctx.dataValueClause())
117116
.usageClauses(ctx.dataUsageClause())
117+
.redefinesClauses(ctx.dataRedefinesClause())
118118
.build();
119119

120120
// TODO: Add check that name does not present in the predefined variables list (? - to check)
@@ -128,6 +128,8 @@ public void defineVariable(@NonNull DataDescriptionEntryFormat1Context ctx) {
128128
checkGlobalFlagFor01Level(variableDefinitionContext);
129129
setValueClauseText(variableDefinitionContext);
130130
saveGlobalVariable(variableDefinitionContext);
131+
checkRedefinesContainsValue(variableDefinitionContext);
132+
checkRedefinesClauseIsSingle(variableDefinitionContext);
131133
// TODO: check the same way that the other clauses are singular or absent
132134

133135
defineVariable(
@@ -264,6 +266,43 @@ public ResultWithErrors<Collection<Variable>> finishDefinitionAnalysis() {
264266
return new ResultWithErrors<>(new ArrayList<>(variables), new ArrayList<>(errors));
265267
}
266268

269+
/**
270+
* Handle and process REDEFINES clause
271+
* @param context is a ParserRuleContext context
272+
*/
273+
public void handleRedefine(DataRedefinesClauseContext context) {
274+
String redefinesName = VisitorHelper.getName(context.dataName());
275+
Locality locality = positions.get(context.dataName().getStart());
276+
if (locality == null) {
277+
return;
278+
}
279+
280+
boolean notFound = true;
281+
Iterator<Variable> iterator = variables.iterator();
282+
if (iterator.hasNext()) {
283+
Variable currentVariable = iterator.next();
284+
285+
while (iterator.hasNext()) {
286+
Variable variable = iterator.next();
287+
if (redefinesName.equals(variable.getName())) {
288+
if (currentVariable.getLevelNumber() != variable.getLevelNumber()) {
289+
addError(messages.getMessage("semantics.levelsMustMatch", redefinesName), positions.get(context.getParent().getStart()));
290+
}
291+
notFound = false;
292+
break;
293+
} else {
294+
if (variable.getLevelNumber() == currentVariable.getLevelNumber()
295+
&& !redefinesName.equals(redefinedVariables.get(variable.getName()))) {
296+
break;
297+
}
298+
}
299+
}
300+
if (notFound) {
301+
addError(messages.getMessage("semantics.redefineImmediatelyFollow", redefinesName), locality);
302+
}
303+
}
304+
}
305+
267306
private String retrieveName(RuleContext context) {
268307
return ofNullable(context)
269308
.map(RuleContext::getText)
@@ -345,6 +384,13 @@ private void addError(String suggestion, Locality locality, ErrorSeverity severi
345384
format("Syntax error defined by %s: %s", getClass().getSimpleName(), error.toString()));
346385
}
347386

387+
private void checkRedefinesContainsValue(VariableDefinitionContext variable) {
388+
if (!(variable.getRedefinesClauses().isEmpty() || variable.getValueClauses().isEmpty())) {
389+
Locality valueLocality = positions.get(variable.getValueClauses().get(0).getStart());
390+
addError(messages.getMessage("semantics.redefinedContainValue", variable.getName()), valueLocality);
391+
}
392+
}
393+
348394
private void checkStartingArea(VariableDefinitionContext variable) {
349395
if ((variable.getNumber() == LEVEL_01 || variable.getNumber() == LEVEL_77)
350396
&& variable.getStarting().getRange().getStart().getCharacter() > AREA_A_FINISH) {
@@ -382,6 +428,10 @@ private void checkValueClauseIsSingle(VariableDefinitionContext variable) {
382428
checkClauseIsSingle(variable.getDefinition(), variable.getValueClauses(), "VALUE");
383429
}
384430

431+
private void checkRedefinesClauseIsSingle(VariableDefinitionContext variable) {
432+
checkClauseIsSingle(variable.getDefinition(), variable.getRedefinesClauses(), "REDEFINES");
433+
}
434+
385435
private void checkUsageClauseIsSingle(VariableDefinitionContext variable) {
386436
checkClauseIsSingle(variable.getDefinition(), variable.getUsageClauses(), "USAGE");
387437
}
@@ -436,6 +486,11 @@ private void defineVariable(
436486
}
437487
variables.push(variable);
438488
});
489+
Optional.ofNullable(variableDefinitionContext.getRedefinesClauses())
490+
.filter(l -> !l.isEmpty())
491+
.ifPresent(b ->
492+
redefinedVariables.put(variableDefinitionContext.getName(),
493+
VisitorHelper.getName(variableDefinitionContext.getRedefinesClauses().get(0).dataName())));
439494
}
440495

441496
private Variable multiTableDataNameMatcher(VariableDefinitionContext variable) {
@@ -655,6 +710,7 @@ private static class VariableDefinitionContext {
655710
List<DataOccursClauseContext> occursClauses;
656711
List<DataValueClauseContext> valueClauses;
657712
List<DataUsageClauseContext> usageClauses;
713+
List<DataRedefinesClauseContext> redefinesClauses;
658714
String valueClauseTest;
659715
String thruValue;
660716
Variable container;

server/src/main/java/org/eclipse/lsp/cobol/core/visitor/VariableUsageDelegate.java

+9
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ public void handleDataName(String dataName, Locality locality, QualifiedDataName
5959
variableUsages.add(new VariableUsage(dataName, parents, locality, parentVariables));
6060
}
6161

62+
/**
63+
* Accumulate variable appearance for analise its definition later.
64+
* @param dataName the variable name
65+
* @param locality the variable text position
66+
*/
67+
public void handleDataName(String dataName, Locality locality) {
68+
variableUsages.add(new VariableUsage(dataName, Collections.emptyList(), locality, Collections.emptyMap()));
69+
}
70+
6271
/**
6372
* Accumulate variable appearance for analise its definition later.
6473
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright (c) 2021 Broadcom.
3+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
4+
*
5+
* This program and the accompanying materials are made
6+
* available under the terms of the Eclipse Public License 2.0
7+
* which is available at https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* Broadcom, Inc. - initial API and implementation
13+
*
14+
*/
15+
package org.eclipse.lsp.cobol.core.visitor;
16+
17+
import lombok.experimental.UtilityClass;
18+
import org.antlr.v4.runtime.RuleContext;
19+
import org.antlr.v4.runtime.tree.TerminalNode;
20+
import org.eclipse.lsp.cobol.core.model.Locality;
21+
import org.eclipse.lsp4j.Range;
22+
23+
import static org.eclipse.lsp.cobol.core.CobolParser.*;
24+
25+
import static java.util.Optional.ofNullable;
26+
import static org.eclipse.lsp.cobol.core.semantics.outline.OutlineNodeNames.FILLER_NAME;
27+
28+
/**
29+
* Utility class for visitor and delegates classes with useful methods
30+
*/
31+
@UtilityClass
32+
public class VisitorHelper {
33+
34+
/**
35+
* Get variable level from TerminalNode object
36+
* @param terminalNode is a TerminalNode
37+
* @return a level of defined variable
38+
*/
39+
public int getLevel(TerminalNode terminalNode) {
40+
String levelNumber = terminalNode.getText();
41+
return Integer.parseInt(levelNumber);
42+
}
43+
44+
/**
45+
* Get name of the statement object
46+
* @param context is a statement context object
47+
* @return a name of the statement
48+
*/
49+
public String getName(EntryNameContext context) {
50+
return ofNullable(context)
51+
.map(EntryNameContext::dataName1)
52+
.map(RuleContext::getText)
53+
.orElse(FILLER_NAME);
54+
}
55+
56+
/**
57+
* Get name of statement context
58+
* @param context is a statement context
59+
* @return a name of statement context
60+
*/
61+
public String getName(DataNameContext context) {
62+
return context.getStart().getText().toUpperCase();
63+
}
64+
65+
/**
66+
* Get interval position of 2 localities
67+
* @param start is a start position
68+
* @param stop is end position
69+
* @return Locality with interval position
70+
*/
71+
public Locality getIntervalPosition(Locality start, Locality stop) {
72+
return Locality.builder()
73+
.uri(start.getUri())
74+
.range(new Range(start.getRange().getStart(), stop.getRange().getEnd()))
75+
.recognizer(VisitorHelper.class)
76+
.copybookId(start.getCopybookId())
77+
.build();
78+
}
79+
}

0 commit comments

Comments
 (0)