Skip to content

feat: make cobol code layout configurable #2248

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

Merged
Merged
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
1 change: 1 addition & 0 deletions clients/cobol-lsp-vscode-extension/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const DIALECT_LIBS = "cobol-lsp.dialect.libs";
export const PATHS_LOCAL_KEY = "paths-local";
export const PATHS_ZOWE = "paths-dsn";
export const PATHS_USS = "paths-uss";
export const COBOL_PRGM_LAYOUT = "cobol-lsp.cobol.program.layout";
export const C4Z_FOLDER: string = ".c4z";
export const COPYBOOKS_FOLDER: string = ".copybooks";
export const GITIGNORE_FILE: string = ".gitignore";
Expand Down
14 changes: 14 additions & 0 deletions clients/cobol-lsp-vscode-extension/src/services/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
SETTINGS_SQL_BACKEND,
SETTINGS_COMPILE_OPTIONS,
DIALECT_LIBS,
COBOL_PRGM_LAYOUT,
} from "../constants";
import cobolSnippets = require("../services/snippetcompletion/cobolSnippets.json");
import { DialectRegistry, DIALECT_REGISTRY_SECTION } from "./DialectRegistry";
Expand All @@ -45,6 +46,7 @@ import {
loadProcessorGroupSqlBackendConfig,
} from "./ProcessorGroups";
import { getProgramNameFromUri } from "./util/FSUtils";
import { integer } from "vscode-languageclient";

/**
* New file (e.g .gitignore) will be created or edited if exits, under project folder
Expand Down Expand Up @@ -341,6 +343,18 @@ export class SettingsService {
return vscode.workspace.getConfiguration().get(SERVER_RUNTIME);
}

public static getCobolProgramLayout():
| {
sequence_length?: integer;
indicator_length?: integer;
area_a_length?: integer;
area_b_length?: integer;
comment_area?: integer;
}
| undefined {
return vscode.workspace.getConfiguration().get(COBOL_PRGM_LAYOUT);
}

private static evaluateVariable(
dataList: string[] | undefined,
variable: string,
Expand Down
18 changes: 11 additions & 7 deletions server/engine/src/main/java/org/eclipse/lsp/cobol/cli/Cli.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
import com.google.gson.*;
import com.google.inject.Guice;
import com.google.inject.Injector;
import java.io.File;
import java.nio.file.Files;
import java.util.*;
import java.util.concurrent.Callable;
import lombok.extern.slf4j.Slf4j;
import org.antlr.v4.runtime.tree.ParseTreeListener;
import org.eclipse.lsp.cobol.cli.di.CliModule;
Expand Down Expand Up @@ -45,15 +49,13 @@
import org.eclipse.lsp.cobol.core.preprocessor.delegates.GrammarPreprocessor;
import org.eclipse.lsp.cobol.core.semantics.CopybooksRepository;
import org.eclipse.lsp.cobol.service.settings.CachingConfigurationService;
import org.eclipse.lsp.cobol.service.settings.layout.CodeLayoutStore;
import org.eclipse.lsp4j.Location;
import picocli.CommandLine;

import java.io.File;
import java.nio.file.Files;
import java.util.*;
import java.util.concurrent.Callable;

/** The Cli class represents a Command Line Interface (CLI) for interacting with the application. */
/**
* The Cli class represents a Command Line Interface (CLI) for interacting with the application.
*/
@CommandLine.Command(description = "COBOL Analysis CLI tools.")
@Slf4j
public class Cli implements Callable<Integer> {
Expand Down Expand Up @@ -196,6 +198,7 @@ private static Pipeline setupPipeline(Injector diCtx, Action action) {
CachingConfigurationService cachingConfigurationService =
diCtx.getInstance(CachingConfigurationService.class);
AstProcessor astProcessor = diCtx.getInstance(AstProcessor.class);
CodeLayoutStore layoutStore = diCtx.getInstance(CodeLayoutStore.class);

Pipeline pipeline = new Pipeline();
pipeline.add(new CompilerDirectivesStage(messageService));
Expand All @@ -211,7 +214,8 @@ private static Pipeline setupPipeline(Injector diCtx, Action action) {
subroutineService,
cachingConfigurationService,
dialectService,
astProcessor));
astProcessor,
layoutStore));
}
return pipeline;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import org.eclipse.lsp.cobol.core.preprocessor.delegates.GrammarPreprocessor;
import org.eclipse.lsp.cobol.lsp.handlers.HandlerUtility;
import org.eclipse.lsp.cobol.service.settings.CachingConfigurationService;
import org.eclipse.lsp.cobol.service.settings.layout.CodeLayoutStore;
import org.eclipse.lsp.cobol.service.utils.ServerTypeUtil;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.Location;
Expand Down Expand Up @@ -89,7 +90,8 @@ public CobolLanguageEngine(
AstProcessor astProcessor,
SymbolsRepository symbolsRepository,
ErrorFinalizerService errorFinalizerService,
BenchmarkService benchmarkService) {
BenchmarkService benchmarkService,
CodeLayoutStore codeLayoutStore) {
this.preprocessor = preprocessor;
this.messageService = messageService;
this.errorFinalizerService = errorFinalizerService;
Expand All @@ -109,7 +111,8 @@ public CobolLanguageEngine(
subroutineService,
cachingConfigurationService,
dialectService,
astProcessor));
astProcessor,
codeLayoutStore));
}

private static AnalysisResult toAnalysisResult(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
import org.eclipse.lsp.cobol.core.CobolParser;
import org.eclipse.lsp.cobol.core.engine.analysis.AnalysisContext;
import org.eclipse.lsp.cobol.core.engine.dialects.DialectService;
import org.eclipse.lsp.cobol.core.engine.pipeline.StageResult;
import org.eclipse.lsp.cobol.core.engine.pipeline.Stage;
import org.eclipse.lsp.cobol.core.engine.pipeline.StageResult;
import org.eclipse.lsp.cobol.core.engine.processor.AstProcessor;
import org.eclipse.lsp.cobol.core.engine.processors.*;
import org.eclipse.lsp.cobol.core.engine.processors.implicit.ImplicitVariablesProcessor;
Expand All @@ -45,6 +45,7 @@
import org.eclipse.lsp.cobol.core.semantics.CopybooksRepository;
import org.eclipse.lsp.cobol.core.visitor.CobolVisitor;
import org.eclipse.lsp.cobol.service.settings.CachingConfigurationService;
import org.eclipse.lsp.cobol.service.settings.layout.CodeLayoutStore;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Range;

Expand All @@ -60,6 +61,7 @@ public class TransformTreeStage implements Stage<ProcessingResult, ParserStageRe
private final CachingConfigurationService cachingConfigurationService;
private final DialectService dialectService;
private final AstProcessor astProcessor;
private final CodeLayoutStore codeLayoutStore;

@Override
public StageResult<ProcessingResult> run(AnalysisContext context, StageResult<ParserStageResult> prevStageResult) {
Expand Down Expand Up @@ -111,7 +113,7 @@ private List<Node> transformAST(AnalysisContext ctx,
CobolParser.StartRuleContext tree) {
CobolVisitor visitor =
new CobolVisitor(copybooksRepository, tokens, ctx.getExtendedDocument(),
messageService, subroutineService, cachingConfigurationService);
messageService, subroutineService, cachingConfigurationService, codeLayoutStore);
List<Node> syntaxTree = visitor.visit(tree);
ctx.getAccumulatedErrors().addAll(visitor.getErrors());
return syntaxTree;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public final class ProcessingConstants {
public static final String CPY_ENTER_TAG = " *>CPYENTER<URI>";
public static final String CPY_URI_CLOSE = "</URI>";
public static final String CPY_EXIT_TAG = NEWLINE + "*>CPYEXIT" + NEWLINE;
public static final int START_INDEX_AREA_B = 11;
public static final int INDICATOR_AREA = 7;

public static final int SEQUENCE_LENGTH = 6;
public static final int INDICATOR_LENGTH = 1;
public static final int AREA_A_LENGTH = 4;
public static final int AREA_B_LENGTH = 61;
public static final int COMMENT_AREA = 8;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,16 @@
*/
package org.eclipse.lsp.cobol.core.preprocessor.delegates.reader;

import static java.util.Optional.ofNullable;
import static org.eclipse.lsp.cobol.common.error.ErrorSeverity.ERROR;
import static org.eclipse.lsp.cobol.core.model.CobolLineTypeEnum.*;

import com.google.common.collect.ImmutableMap;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
Expand All @@ -25,20 +32,13 @@
import org.eclipse.lsp.cobol.common.error.SyntaxError;
import org.eclipse.lsp.cobol.common.message.MessageService;
import org.eclipse.lsp.cobol.common.model.Locality;
import org.eclipse.lsp.cobol.core.preprocessor.CobolLine;
import org.eclipse.lsp.cobol.core.model.*;
import org.eclipse.lsp.cobol.core.preprocessor.ProcessingConstants;
import org.eclipse.lsp.cobol.core.preprocessor.CobolLine;
import org.eclipse.lsp.cobol.service.settings.layout.CobolProgramLayout;
import org.eclipse.lsp.cobol.service.settings.layout.CodeLayoutStore;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.Optional.ofNullable;
import static org.eclipse.lsp.cobol.common.error.ErrorSeverity.ERROR;
import static org.eclipse.lsp.cobol.core.model.CobolLineTypeEnum.*;

/**
* Preprocessor, which converts strings with COBOL code into a specific entity; analyzes and
* processes line indicators. This implementation checks if the lines match the given format and
Expand All @@ -48,13 +48,11 @@
@Slf4j
@Singleton
public class CobolLineReaderImpl implements CobolLineReader {
private static final int INDICATOR_AREA_INDEX = 6;
private static final int MAX_LINE_LENGTH = 80;
private static final Pattern COBOL_LINE_PATTERN =
Pattern.compile(
"^(?<sequence>.{0,6})(?<indicator>.?)(?<contentA>.{0,4})(?<contentB>.{0,61})(?<comment>.{0,8})(?<extra>.*)$");
//TODO: check directive rules for HP tandem/GNU
private static final Pattern COMPILER_DIRECTIVE_LINE =
Pattern.compile("(?i)(.{0,6} +|\\s*+)(?<directives>(CBL|PROCESS) .+)");

// TODO: check these for GNU and HP. Also make it customizable
private static final Map<String, CobolLineTypeEnum> INDICATORS =
new ImmutableMap.Builder<String, CobolLineTypeEnum>()
.put("*", COMMENT)
Expand All @@ -68,10 +66,12 @@ public class CobolLineReaderImpl implements CobolLineReader {
.build();

private final MessageService messageService;
private final CodeLayoutStore layoutStore;

@Inject
public CobolLineReaderImpl(MessageService messageService) {
public CobolLineReaderImpl(MessageService messageService, CodeLayoutStore layoutStore) {
this.messageService = messageService;
this.layoutStore = layoutStore;
}

@NonNull
Expand Down Expand Up @@ -106,7 +106,7 @@ private ResultWithErrors<CobolLine> parseLine(
List<SyntaxError> errors = new ArrayList<>();
CobolLine cobolLine;

Matcher usualLine = COBOL_LINE_PATTERN.matcher(line);
Matcher usualLine = layoutStore.getCodeLayout().getCobolLinePattern().matcher(line);
Matcher directivesLine = COMPILER_DIRECTIVE_LINE.matcher(line);
if (directivesLine.matches()) {
cobolLine =
Expand Down Expand Up @@ -155,13 +155,15 @@ private ResultWithErrors<CobolLine> processNormalLine(

private String cleanupString(@NonNull String line, int contentStart) {
String lineWithoutSequence = StringUtils.repeat(' ', contentStart) + line;
return lineWithoutSequence.length() > 72
? lineWithoutSequence.substring(0, 72)
CobolProgramLayout codeLayout = layoutStore.getCodeLayout();
return lineWithoutSequence.length() > codeLayout.getSourceCodeLength()
? lineWithoutSequence.substring(0, codeLayout.getSourceCodeLength())
: lineWithoutSequence;
}

private ResultWithErrors<CobolLineTypeEnum> determineType(
String indicatorArea, String uri, int lineNumber) {
CobolProgramLayout codeLayout = layoutStore.getCodeLayout();
return ofNullable(INDICATORS.get(indicatorArea))
.map(it -> new ResultWithErrors<>(it, Collections.emptyList()))
.orElseGet(
Expand All @@ -173,22 +175,22 @@ private ResultWithErrors<CobolLineTypeEnum> determineType(
uri,
messageService.getMessage("CobolLineReaderImpl.incorrectLineFormat"),
lineNumber,
INDICATOR_AREA_INDEX,
INDICATOR_AREA_INDEX + 1))));
codeLayout.getSequenceLength(), codeLayout.getSequenceLength() + 1))));
}

@NonNull
private Optional<SyntaxError> checkLineLength(
@NonNull String line, @NonNull String uri, int lineNumber) {
if (line.length() <= 80) {
int maxLineLength = layoutStore.getCodeLayout().getMaxLineLength();
if (line.length() <= maxLineLength) {
return Optional.empty();
}
return Optional.of(
createError(
uri,
messageService.getMessage("CobolLineReaderImpl.longLineMsg"),
messageService.getMessage("CobolLineReaderImpl.longLineMsg", maxLineLength),
lineNumber,
MAX_LINE_LENGTH,
maxLineLength,
line.length()));
}

Expand All @@ -208,7 +210,8 @@ private Optional<SyntaxError> checkSequenceArea(

private boolean isSequenceNumberFormatCorrect(String line, int contentStart) {
// issue error the sequence must start with a number.
return contentStart < ProcessingConstants.INDICATOR_AREA
CobolProgramLayout codeLayout = layoutStore.getCodeLayout();
return contentStart < (codeLayout.getIndicatorLength() + codeLayout.getSequenceLength())
|| StringUtils.isBlank(line.substring(0, contentStart))
|| Character.isDigit(line.charAt(0));
}
Expand Down
Loading