Skip to content

Commit 8189c7f

Browse files
committed
feat: Support for redefines eclipse-che4z#867
Signed-off-by: Leonid Baranov <[email protected]>
1 parent a9f5b3d commit 8189c7f

13 files changed

+472
-41
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

+57-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+
.redefinedClauses(ctx.dataRedefinesClause())
118118
.build();
119119

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

133134
defineVariable(
@@ -264,6 +265,45 @@ public ResultWithErrors<Collection<Variable>> finishDefinitionAnalysis() {
264265
return new ResultWithErrors<>(new ArrayList<>(variables), new ArrayList<>(errors));
265266
}
266267

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

388+
private void checkRedefinedContainValue(VariableDefinitionContext variable) {
389+
if (!variable.getRedefinedClauses().isEmpty() && !variable.getValueClauses().isEmpty()) {
390+
Locality valueLocality = positions.get(variable.getValueClauses().get(0).getStart());
391+
addError(messages.getMessage("CobolVisitor.redefinedContainValue", variable.getName()), valueLocality);
392+
}
393+
}
394+
348395
private void checkStartingArea(VariableDefinitionContext variable) {
349396
if ((variable.getNumber() == LEVEL_01 || variable.getNumber() == LEVEL_77)
350397
&& variable.getStarting().getRange().getStart().getCharacter() > AREA_A_FINISH) {
@@ -436,6 +483,11 @@ private void defineVariable(
436483
}
437484
variables.push(variable);
438485
});
486+
Optional.ofNullable(variableDefinitionContext.getRedefinedClauses())
487+
.filter(l -> !l.isEmpty())
488+
.ifPresent(b ->
489+
redefinedVariables.put(variableDefinitionContext.getName(),
490+
VisitorHelper.getName(variableDefinitionContext.getRedefinedClauses().get(0).dataName())));
439491
}
440492

441493
private Variable multiTableDataNameMatcher(VariableDefinitionContext variable) {
@@ -655,6 +707,7 @@ private static class VariableDefinitionContext {
655707
List<DataOccursClauseContext> occursClauses;
656708
List<DataValueClauseContext> valueClauses;
657709
List<DataUsageClauseContext> usageClauses;
710+
List<DataRedefinesClauseContext> redefinedClauses;
658711
String valueClauseTest;
659712
String thruValue;
660713
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,94 @@
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.ParserRuleContext;
19+
import org.antlr.v4.runtime.RuleContext;
20+
import org.antlr.v4.runtime.tree.TerminalNode;
21+
import org.eclipse.lsp.cobol.core.model.Locality;
22+
import org.eclipse.lsp4j.Range;
23+
24+
import static org.eclipse.lsp.cobol.core.CobolParser.*;
25+
26+
import static java.util.Optional.ofNullable;
27+
import static org.eclipse.lsp.cobol.core.semantics.outline.OutlineNodeNames.FILLER_NAME;
28+
29+
/**
30+
* Utility class for visitor and delegates classes with useful methods
31+
*/
32+
@UtilityClass
33+
public class VisitorHelper {
34+
35+
/**
36+
* Get variable level from TerminalNode object
37+
* @param terminalNode is a TerminalNode
38+
* @return a level of defined variable
39+
*/
40+
public int getLevel(TerminalNode terminalNode) {
41+
String levelNumber = terminalNode.getText();
42+
return Integer.parseInt(levelNumber);
43+
}
44+
45+
/**
46+
* Get name of the statement object
47+
* @param context is a statement context object
48+
* @return a name of the statement
49+
*/
50+
public String getName(EntryNameContext context) {
51+
return ofNullable(context)
52+
.map(EntryNameContext::dataName1)
53+
.map(RuleContext::getText)
54+
.orElse(FILLER_NAME);
55+
}
56+
57+
/**
58+
* Get name of statement context
59+
* @param context is a statement context
60+
* @return a name of statement context
61+
*/
62+
public String getName(DataNameContext context) {
63+
return context.getStart().getText().toUpperCase();
64+
}
65+
66+
/**
67+
* Get interval position of 2 localities
68+
* @param start is a start position
69+
* @param stop is end position
70+
* @return Locality with interval position
71+
*/
72+
public Locality getIntervalPosition(Locality start, Locality stop) {
73+
return Locality.builder()
74+
.uri(start.getUri())
75+
.range(new Range(start.getRange().getStart(), stop.getRange().getEnd()))
76+
.recognizer(CobolVisitor.class)
77+
.copybookId(start.getCopybookId())
78+
.build();
79+
}
80+
81+
/**
82+
* Get variable level from a statement context object
83+
* @param context is a statement context object
84+
* @return a level of defined variable
85+
*/
86+
public int getLevel(ParserRuleContext context) {
87+
try {
88+
return Integer.parseInt(context.getStart().getText());
89+
} catch (NumberFormatException e) {
90+
return 0;
91+
}
92+
}
93+
94+
}

0 commit comments

Comments
 (0)