Skip to content

Commit

Permalink
wip: implement some rules with nester statements for experimental parser
Browse files Browse the repository at this point in the history
  • Loading branch information
ishche committed Jun 6, 2024
1 parent 6d162f7 commit 250b082
Show file tree
Hide file tree
Showing 55 changed files with 3,586 additions and 196 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,8 @@

package org.eclipse.lsp.cobol.core.visitor;

import static java.util.Collections.emptyList;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;
import static org.antlr.v4.runtime.Lexer.HIDDEN;
import static org.eclipse.lsp.cobol.common.OutlineNodeNames.FILLER_NAME;
import static org.eclipse.lsp.cobol.common.VariableConstants.*;
import static org.eclipse.lsp.cobol.core.CobolParser.*;
import static org.eclipse.lsp.cobol.core.visitor.VisitorHelper.*;

import com.google.common.collect.ImmutableList;

import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import lombok.Getter;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RuleContext;
Expand All @@ -44,13 +27,13 @@
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.eclipse.lsp.cobol.common.SubroutineService;
import org.eclipse.lsp.cobol.common.dialects.CobolDialect;
import org.eclipse.lsp.cobol.common.dialects.CobolProgramLayout;
import org.eclipse.lsp.cobol.common.error.ErrorSeverity;
import org.eclipse.lsp.cobol.common.error.ErrorSource;
import org.eclipse.lsp.cobol.common.error.SyntaxError;
import org.eclipse.lsp.cobol.common.mapping.ExtendedDocument;
import org.eclipse.lsp.cobol.common.message.MessageService;
import org.eclipse.lsp.cobol.common.model.*;
import org.eclipse.lsp.cobol.common.model.SectionType;
import org.eclipse.lsp.cobol.common.model.tree.*;
import org.eclipse.lsp.cobol.common.model.tree.statements.SetToBooleanStatement;
import org.eclipse.lsp.cobol.common.model.tree.statements.SetToOnOffStatement;
Expand All @@ -61,30 +44,39 @@
import org.eclipse.lsp.cobol.common.utils.ImplicitCodeUtils;
import org.eclipse.lsp.cobol.common.utils.StringUtils;
import org.eclipse.lsp.cobol.core.*;
import org.eclipse.lsp.cobol.core.CobolDataDivisionParser;
import org.eclipse.lsp.cobol.core.CobolIdentificationDivisionParser;
import org.eclipse.lsp.cobol.core.CobolParser;
import org.eclipse.lsp.cobol.core.CobolParserBaseVisitor;
import org.eclipse.lsp.cobol.core.semantics.CopybooksRepository;
import org.eclipse.lsp.cobol.dialects.ibm.experimental.visitors.CobolDataDivisionVisitor;
import org.eclipse.lsp.cobol.dialects.ibm.experimental.visitors.CobolIdentificationDivisionVisitor;
import org.eclipse.lsp.cobol.dialects.ibm.experimental.visitors.CobolProcedureDivisionVisitor;
import org.eclipse.lsp.cobol.service.settings.CachingConfigurationService;
import org.eclipse.lsp.cobol.common.dialects.CobolProgramLayout;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.slf4j.Logger;

import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static java.util.Collections.emptyList;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;
import static org.antlr.v4.runtime.Lexer.HIDDEN;
import static org.eclipse.lsp.cobol.common.OutlineNodeNames.FILLER_NAME;
import static org.eclipse.lsp.cobol.common.VariableConstants.*;
import static org.eclipse.lsp.cobol.core.CobolParser.*;
import static org.eclipse.lsp.cobol.core.visitor.VisitorHelper.*;

/**
* This extension of {@link CobolParserBaseVisitor} applies the semantic analysis based on the
* abstract syntax tree built by {@link CobolParser}. It requires a semantic context with defined
* elements to add the usages or throw a warning on an invalid definition. If there is a misspelled
* keyword, the visitor finds it and throws a warning.
*/
@Slf4j
public class CobolVisitor extends CobolParserBaseVisitor<List<Node>> {

@Getter
private static final Logger LOG = org.slf4j.LoggerFactory.getLogger(CobolVisitor.class);
protected final List<SyntaxError> errors = new ArrayList<>();
protected final CopybooksRepository copybooks;
protected final CommonTokenStream tokenStream;
Expand Down Expand Up @@ -1488,4 +1480,8 @@ private Locality locationToLocality(Location location) {
.copybookId(copybooks.getCopybookIdByUri(location.getUri()))
.build();
}

public List<SyntaxError> getErrors() {
return this.errors;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,8 @@ default <T extends CstNode> List<T> list(Class<T> clazz) {
for (CstNode child : getChildren()) {
if (clazz.isInstance(child)) {
result.add((T) child);
} else {
result.addAll(child.list(clazz));
}
result.addAll(child.list(clazz));
}
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,13 @@ public Token next() {
}
return start.produceToken(TokenType.OTHER);
}
if (match('*')) {
forward();
if (match('*')) {
forward();
}
return start.produceToken(TokenType.OTHER);
}
return cobolWord(start);
}

Expand Down Expand Up @@ -171,6 +178,9 @@ private Token cobolWord(Position start) {
}
} while (!isAtEnd() && isCobolWordSymbol());

if (currentPosition.index - start.index == 1 && source.charAt(currentPosition.index - 1) == '.') {
return start.produceToken(TokenType.DOT);
}
return start.produceToken(TokenType.COBOL_WORD);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public enum TokenType {
NUMBER_LITERAL,
COBOL_WORD,
NEW_LINE,
DOT,
OTHER,
EOF
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@
import org.eclipse.lsp.cobol.rules.environment.EnvironmentDivisionRule;
import org.eclipse.lsp.cobol.rules.identification.IdentificationDivisionRule;
import org.eclipse.lsp.cobol.rules.procedure.*;
import org.eclipse.lsp.cobol.rules.procedure.statements.ImperativeStatementRule;
import org.eclipse.lsp.cobol.rules.procedure.statements.MoveRule;
import org.eclipse.lsp.cobol.rules.procedure.statements.PerformRule;
import org.eclipse.lsp.cobol.rules.procedure.statements.*;

/** A container for COBOL language grammar rules. */
public class CobolLanguage {
Expand All @@ -42,10 +40,30 @@ public CobolLanguage() {
languageRules.put(IdentificationDivisionRule.class, new IdentificationDivisionRule());
languageRules.put(SectionRule.class, new SectionRule());
languageRules.put(SentenceRule.class, new SentenceRule());
languageRules.put(AddRule.class, new AddRule());
languageRules.put(MultiplyRule.class, new MultiplyRule());
languageRules.put(ComputeRule.class, new ComputeRule());
languageRules.put(PerformRule.class, new PerformRule());
languageRules.put(IfRule.class, new IfRule());
languageRules.put(MoveRule.class, new MoveRule());
languageRules.put(ImperativeStatementRule.class, new ImperativeStatementRule());
languageRules.put(EvaluateRule.class, new EvaluateRule());
languageRules.put(StringRule.class, new StringRule());
languageRules.put(UnstringRule.class, new UnstringRule());
languageRules.put(SearchRule.class, new SearchRule());
languageRules.put(SubtractRule.class, new SubtractRule());
languageRules.put(CallRule.class, new CallRule());
languageRules.put(DivideRule.class, new DivideRule());
languageRules.put(StartRule.class, new StartRule());
languageRules.put(ReadRule.class, new ReadRule());
languageRules.put(ReturnRule.class, new ReturnRule());
languageRules.put(DeleteRule.class, new DeleteRule());
languageRules.put(WriteRule.class, new WriteRule());
languageRules.put(RewriteRule.class, new RewriteRule());

// Fragments
languageRules.put(ConditionExpressionRule.class, new ConditionExpressionRule());
languageRules.put(ArithmeticExpressionRule.class, new ArithmeticExpressionRule());
languageRules.put(IdentifierRule.class, new IdentifierRule());

//// Reserved Words P. 761
// Arithmetic operator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
import org.eclipse.lsp.cobol.parser.hw.lexer.Token;
import org.eclipse.lsp.cobol.parser.hw.lexer.TokenType;

import java.util.Objects;

/**
* COBOL language grammar rule class.
*/
Expand Down Expand Up @@ -66,7 +64,7 @@ private void synchronize(ParsingContext ctx) {
}

private boolean isContinuationSymbol(Token t) {
return Objects.equals(t.getLexeme(), ".");
return t.getType() == TokenType.DOT;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* 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.rules.procedure;

import org.eclipse.lsp.cobol.parser.hw.ParsingContext;
import org.eclipse.lsp.cobol.parser.hw.lexer.TokenType;
import org.eclipse.lsp.cobol.rules.CobolLanguage;
import org.eclipse.lsp.cobol.rules.LanguageRule;

/** p. 266 */
public class ArithmeticExpressionRule implements LanguageRule {

@Override
public void parse(ParsingContext ctx, CobolLanguage language) {
arithmeticExpression(ctx, language);
}

private void arithmeticExpression(ParsingContext ctx, CobolLanguage language) {
if (ctx.match("(")) {
ctx.consume("(");
do {
arithmeticExpression(ctx, language);
} while (!ctx.match(")"));
ctx.consume(")");
ctx.spaces();
// p. 267
if (ctx.match("+", "-", "*", "/", "**")) {
ctx.or("+", "-", "*", "/", "**");
ctx.spaces();
arithmeticExpression(ctx, language);
}
return;
}

if (ctx.getLexer().peek().getType() == TokenType.NUMBER_LITERAL) {
ctx.consume();
ctx.spaces();
} else {
language.parseRule(IdentifierRule.class, ctx);
}
// p. 267
if (ctx.match("+", "-", "*", "/", "**")) {
ctx.or("+", "-", "*", "/", "**");
ctx.spaces();
arithmeticExpression(ctx, language);
}
}

@Override
public boolean tryMatch(ParsingContext ctx, CobolLanguage language) {
// An arithmetic expression can consist of any of the following items:
// 1. An identifier described as a numeric elementary item (including numeric functions)
if (language.tryMatchRule(IdentifierRule.class, ctx)) {
return true;
}
// 2. A numeric literal
if (ctx.getLexer().peek().getType() == TokenType.NUMBER_LITERAL) {
return true;
}
// 3. The figurative constant ZERO
if (ctx.match("ZERO")) {
return true;
}
// 4. Identifiers and literals, as defined in items 1, 2, and 3, separated by arithmetic
// operators
// 5. Two arithmetic expressions, as defined in items 1, 2, 3, or 4, separated by an arithmetic
// operator
// 6. An arithmetic expression, as defined in items 1, 2, 3, 4, or 5, enclosed in parentheses
if (ctx.match("(")) {
return true;
}
// Any arithmetic expression can be preceded by a unary operator.
if (ctx.match("+", "-")) {
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,26 +78,7 @@ public void parse(ParsingContext ctx, CobolLanguage language) {
}

private void conditionOperand(ParsingContext ctx, CobolLanguage language) {
if (ctx.match("(")) {
ctx.consume("(");
do {
conditionOperand(ctx, language);
} while (!ctx.match(")"));
ctx.consume(")");
ctx.spaces();
return;
}
if (ctx.match("FUNCTION")) {
functionIdentifier(ctx);
return;
}
identifier(ctx);
// p. 267
if (ctx.match("+", "-", "*", "/", "**")) {
ctx.or("+", "-", "*", "/", "**");
ctx.spaces();
conditionOperand(ctx, language);
}
language.parseRule(ArithmeticExpressionRule.class, ctx);
}

private void compositeCondition(ParsingContext ctx, CobolLanguage language) {
Expand Down Expand Up @@ -177,30 +158,36 @@ private void functionIdentifier(ParsingContext ctx) {
// TODO: optional reference-modifier
}

// TODO: move to rule?
private void identifier(ParsingContext ctx) {
ctx.consume();
ctx.spaces();
if (ctx.match("(")) {
ctx.consume("(");
ctx.spaces();
do {
ctx.consume(); // indexes
ctx.spaces();
} while (!ctx.match(")"));
ctx.consume(")"); // index
ctx.spaces();
}
}

@Override
public boolean tryMatch(ParsingContext ctx, CobolLanguage language) {
// TODO TBD
return isClassCondition(ctx);
// operand-1
// Can be an identifier, literal, function-identifier, arithmetic expression, or index-name.
if (language.tryMatchRule(ArithmeticExpressionRule.class, ctx)) {
return true;
}
if (ctx.matchSeq(null, "IS")
|| ctx.matchSeq(null, "GREATER")
|| ctx.matchSeq(null, "NOT", "GREATER")
|| ctx.matchSeq(null, ">")
|| ctx.matchSeq(null, "NOT", ">")
|| ctx.matchSeq(null, "<")
|| ctx.matchSeq(null, "NOT", "<")
|| ctx.matchSeq(null, "=")
|| ctx.matchSeq(null, "NOT", "=")
|| ctx.matchSeq(null, "LESS")
|| ctx.matchSeq(null, "NOT", "LESS")
|| ctx.matchSeq(null, "EQUAL")
|| ctx.matchSeq(null, "NOT", "EQUAL")
|| ctx.matchSeq(null, "<=")
|| ctx.matchSeq(null, ">=")) {
return true;
}
return false;
// return isClassCondition(ctx);
}

// P. 268
private boolean isClassCondition(ParsingContext ctx) {
return true;
}
// private boolean isClassCondition(ParsingContext ctx) {
// return true;
// }
}
Loading

0 comments on commit 250b082

Please sign in to comment.