Skip to content

How to Create Your Own Dialect

Aman Prashant edited this page Jan 10, 2024 · 8 revisions

A good example of a dialect is the IDMS dialect

A dialect extension has 2 parts:

The VS Code Extension logic is quite straightforward: the extension just registers itself in the COBOL LS Support VS Code Extension using a path to a jar file (idms path example), a path to snippets, and other information about a dialect (idms dialect extension example)

The main dialect logic is situated in the jar file

How to build a jar file for a dialect extension

If you have your own COBOL dialect, there are probably some instructions that cannot be parsed by the regular COBOL parser, which need to be parsed and substituted with spaces by your dialect logic. There might also be a new type of copybook that must be properly resolved and injected into the COBOL program.

1. Prepare the maven java module

To create a new module with dialect logic you need to add a dependency for the common module because for now, the only way to create a new dialect is to implement the CobolDialect interface.

        <dependency>
            <groupId>org.eclipse.lsp.cobol</groupId>
            <artifactId>common</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>provided</scope>
        </dependency>

This library will be included in the final engine build, so it can have a provided scope.

2. CobolDialect implementation

The core interface that needs to be implemented for a new dialect is the CobolDialect interface. The final .jar file with a dialect implementation will be picked up by the engine using URLClassLoader, so the .jar file and CobolDialect implementation class must follow the naming convention to be picked up.

CobolDialect has the following methods that need to be implemented/overridden:

  • getName - returns the name of the dialect
  • extend - extends extended source with dialect's copybooks if needed. Use the CopybookService to resolve copybooks
  • processText - process extended source and returns list of created nodes with list of errors (if any)
  • getProcessors - returns a list of processors that will be applied to the AST on the defined processing stage
  • runBefore - returns a list of dialects. The current dialect will be processed before this list of dialects
  • getKeywords - returns a map with keyword/description pairs
  • getSettingsSections - returns a list of settings that will be requested from a VS Code side and placed into the dialect processing context before processing
  • getWatchingFolderSettings - returns a list of settings sections that hold dialect folders

Take a look into Idm dialect implementation for more details

2.1. Naming convention and a dialect constructor convention

For the fastest picking up a new dialect logic implemented in the separate .jar file, this file and a class with the CobolDialect implementation must follow the naming convention.

  • the .jar file must have the dialect- prefix. For example: dialect-idms.jar
  • the CobolDialect interface implementation class must have the Dialect suffix. For example: IdmsDialect
  • the CobolDialect implementation class must have a constructor with the following signature:

It is needed to pass to the dialect CopybookService and MessageService services

public final class SomeDialect implements CobolDialect {
  public SomeDialect(CopybookService copybookService, MessageService messageService) {
    ...
  }
...
}

For more information see the DialectDiscoveryFolderService class, which implements a picking-up dialect logic

2.2. CopybookService interface

CopybookService is a service that provides copybook-resolving logic and is needed to get the content of the requested copybook with taking into account copybook processing settings and dialect folders

  • invalidateCache - removes all cached copybooks
  • resolve - returns a copybook content with a URI of the copybook, or blank structure if the copybook was not resolved
  • store - stores a copybook in the cache, mostly used for testing purposes, because the resolve copybook method already has this logic and after resolving the copybook you can be sure that this copybook was stored in the cache

2.3. MessageService interface

MessageService is used to retrieve a message for the error or warning using a string key

  • getMessage returns a message for a given key

2.4. DialectProcessingContext

Dialect processing context is a context that holds all needed objects for dialect processing. There is a copybook config, document URI, etc. Also, the context has an ExtendedSource object that is used to extend or modify COBOL text

2.5. Mapping and ExtendedSource

When dialects extend COBOL text with dialect copybooks we need to track all changes to be able to answer the question "where this token come from?". The ExtendedSource object serves to keep the COBOL text consistent and to track changes and extensions of the COBOL text.

It has the following methods:

  • replace - replaces in the document defined range with the given copybook document. For example, when you are resolving a copybook you probably want to replace a copybook statement with a copybook content
  • extend - the same as replace but with adjusting copybook statement range
  • insert - inserts a copybook document after the given line
  • extendedText - generates extended text based on extends, inserts, and replacements of the original COBOL text

and others

2.6. DialectOutcome and ResultWithErrors

DialectOutcome is a result of CobolDialect::processText wrapped to the ResultWithErrors object. It contains a list of created nodes by current dialect and a modified DialectProcessingContext. And ResultWithErrors contains a list of generated errors (if any).


How to build your dialect extension

Now when you have a properly generated .jar file with CobolDialect implementation inside, you need to wrap it to the VS Code Extension and implement simple logic to register your dialect to the main COBOL LS Extension.

A good example is Idms Dialect Support Extension

Here is a folder for a .jar file with your dialect logic. And simple Idms Dialect Extension logic:

  • import COBOL LS api:
import { getV1Api } from "@code4z/cobol-dialect-api";
  • use returned API to register a new dialect:
  const extensionId = context.extension.id;
  const extensionUri = context.extensionUri;
  const snippets = vscode.Uri.joinPath(extensionUri, "snippets.json");
  const jar = vscode.Uri.joinPath(
    extensionUri,
    "server",
    "jar",
    "dialect-idms.jar",
  );
  const v1Api = await getV1Api(extensionId);
  if (v1Api instanceof Error) {
    vscode.window.showErrorMessage(v1Api.toString());
    return;
  }
  const unregister = await v1Api.registerDialect({
    name: "IDMS",
    description: "IDMS dialect support",
    snippets,
    jar,
  });
  if (unregister instanceof Error) {
    vscode.window.showErrorMessage(unregister.toString());
    return;
  }
  unregisterDialect = unregister;
}