📘 Ch3. The Langium Language Workbench
Langium is an open-source language engineering framework developed by TypeFox and hosted by the Eclipse Foundation. It can be used to implement a software language in Typescript, which can then be deployed in a web environment or as an extension for the Visual Studio Code IDE.
| Langium is the "spiritual successor" of an older language engineering framework called Xtext, which can be used to implement a software language in Java and to generate a powerful integration of this language in the Eclipse IDE. |
| This chapter introduces very general aspects of Langium, but does not explain how to implement a complete DSL with Langium. Implement language components with Langium will be progressively presented in the next three chapters. |
1. Langium overview
The Langium framework is organized around three main components:
-
A meta-language, called the Langium Grammar Language, which can be used to create a Langium grammar. A Langium grammar defines both the abstract syntax and the concrete syntax of the software language being developed. The abstract syntax is written as a set of data types (with a notation similar to TypeScript), while the concrete syntax is defined in the form of a grammar rules (with a notation similar to EBNF).
-
A generator, which takes a Langium grammar as input and produces a set of Typescript language components as output (detailed below).
-
A Typescript library used by both the generator and the generated language components.
Using the Langium framework to create a new software language can be depicted as follows:
-
At the top, the software language engineer uses the Langium Grammar Language to define both the abstract and concrete syntaxes of the developed language.
-
The resulting Langium grammar is given to the Langium generator, which produces as output a wide array of artifacts:
-
A set of Abstract syntax data types, in the form of Typescript types, for all the concepts of the abstract syntax,
-
A parser, able to read a text file and to produce an AST as defined by the grammar rules,
-
A set of editing services which are essentially all the logic of a code editor for the language being developed (syntax highlighting, error highlighting, autocompletion, outline view, etc.).
-
A language API, which can be used as a single HTTP entrypoint to access most languages components. The services of this API follow the standardized Language Server Protocol (LSP).
-
A Visual Studio Code extension which can be used to easily deploy the software language editor in any Visual Studio Code IDE. This extension is mostly an adapter that interacts with the language API to provide most services.
-
-
Finally, some parts of the language implementation must be developed manually by the language engineer. This includes:
-
the implementation of the semantics, either in the form of a code generator (if the DSL is implemented as a compiler) or in the form of an AST interpreter (if the DSL is implemented as an interpreter),
-
the implementation of a validator (in the form of validation rules executed on the AST),
-
the implemention of a Command Line Interface (CLI) to call specific language services (such as the code generator).
-
-
Once the language has been fully implemented, the domain expert (at the bottom) can create and edit programs using the VSCode extension, and can call specific services (eg. the code generator) using the CLI.
2. Installing a Langium development environment
| This section assumes you are running a Linux-based environment. |
2.1. Prerequisites
Developping a Langium DSL requires a Javascript/Typescript development environment:
-
Make sure you have
npmandnode(at least version 20, if possible higher) available in your system:-
Run
npm --versionto checknpmis installed, -
Run
node --versionto checknodeis installed with a compatible version.
-
-
Make sure you have configured npm to allow installation of packages globally:
-
Run this command:
npm config set prefix ~/.local, -
Modify your
.bashrcfile and add:export PATH=~/.local/bin:$PATH, -
Restart your terminal.
-
2.2. Langium Yeoman project generator
Langium provides a Yeoman generator which is mandatory to quickstart a Langium project.
Yeoman can be installed on your system with npm with the following command:
npm install -g yo generator-langium
The -g option requires that you first configure your system to install npm packages in your home directory, as written in this page of the npm documentation.
|
2.3. Langium VScode extension
Langium provides an official Visual Studio Code extension that includes a complete textual editor for the Langium Grammar Language (.langium files).
You can install this extension directly using the Extensions pane of Visual Studio Code.
3. Preparing a Langium project
To generate a new folder that will contain your new Langium project, run this Yeoman command:
yo langium
This will ask you different questions about your Langium project, such as the name of the software language being developed, the file extension for this language, and which development environments should be supported (CLI, Visual Studio Code, web, etc.). For more information, refer to this page of the Langium documentation.
|
If you are unsure, you can always answer yes to all questions. However note that the question "Generate example project?" is different from others: it will create an example DSL that will be a pain to replace with your own DSL! In most cases you will prefer to answer No to this question. |
After generation of your base Langium project, you obtain a Langium project structure, which can be depicted like this:
This structure is centered on the folder packages, which contains in the following subfolders (each containing a different npm package):
-
language: Contains the main components of the software language, in particular:-
langium-config.json: Contains metadata about your language, including the language name, the file extension, or the path to the main.langiumfile. If multiple.langiumfiles are used, they must be imported from the.langiumfile referenced inlangium-config.json -
src: Contains the.langiumfiles defining the Langium grammar of the language, which itself defines the abstract syntax and the concrete syntax of the language (will be presented in the next section), with one subfolder:-
generated: Contains files generated by the Langium generator from the.langiumfiles, including theast.tsfile that contains the abstract syntax data types.
-
-
test: Contains test cases for the language.
-
-
cli: contains a base implementation of a Command Line Interface (CLI) for the language, which by default only includes a placeholder for a code generator where the semantics of the language can be implemented. -
extension: Contains the code of the Visual Studio Code extension of the language, which is essentially a code editor for the DSL.
| Some of the subfolders may be absent, depending on the answers given to the Yeoman generator. |
It is important to note that the npm components cli and extension are both strongly dependent on the language component, which is logical given that all the "business logic" of the DSL (syntax, semantics, editing services, etc.) is implemented in the language component.
4. Working on a Langium project
4.1. Building a Langium project
The package.json file of a Langium project provides the following actions available using npm:
-
npm run langium:generate: Call the Langium Generator, which will compile the.langiumfiles into TypeScript, and into a few other artifacts (must be called after any change to a.langiumfile). -
npm run build: Compiles all the Typescript code (must be called after any code change, and after any call to the Langium Generator).
Hence, to fully build a Langium project, the following command can be used:
npm run langium:generate && npm run build
4.2. Manual testing of a Langium DSL
| Langium also provides a way to perform automated testing of a DSL, but this course will not cover this possibility. If you would like to try, you can answer Yes when Yeoman asks if you would like your project to include language tests. |
4.2.1. Manual testing of the Visual Studio Code extension
Once a Langium project is built, it is possible to manually test the resulting Visual Studio Code extension (and the editor for the DSL included within).
Open the "Run and Debug" pane:
Observe that two run configurations have been prepared by Yeoman when you initialized your project:
-
"Run Extension" allows you to test your extension by starting a second Visual Studio Code window in which your language in developpment will be installed as a Visual Studio Code extension.
-
"Attach to Language Server " allows you to debug your language implementation by attaching a TypeScript debugger to an already running extension of your language (requires doing Run Extension first).
When you use "Run Extension", a second "development mode" Visual Studio Code window appears in an empty workspace. In this empty workspace, create a new file using the file extension of your Langium language (important). This will open the customized editor for your language.
We are implementing a DSL called the State Machines DSL using Langium.
We would like to manually test and debug the Visual Studio Code extension, especially the editor of the DSL. We execute the "Run Extension" run configuration, which starts a new Visual Studio Code instance where the State Machines DSL editor can be tested:
4.2.2. Manual testing of the CLI
When a Langium project is built, a file named ./packages/cli/bin/cli.js is produced which allows running the CLI.
Hence to manually test the CLI, we can simply run this file (here with the action generate):
node ./packages/cli/bin/cli.js generate <path to program to compile>
You can also make your CLI available globally in your system by running the npm link command in your project folder.
This should create a command <language name>-cli available in your console, which is a symbolic link to the ./packages/cli/bin/cli.js file of your language.
|
We are implementing a DSL called the State Machines DSL using Langium.
We would like to manually test and debug the CLI of the DSL, especially the code generator that can be called using the CLI.
We prepare a State Machines program in a file examples/helloworld.sm, and we test the DSL CLI using this command:
node ./packages/cli/bin/cli.js generate examples/helloworld.sm
5. Packaging and distributing a Langium DSL
Once a Langium DSL is ready to use, the last step is to properly package the DSL to install it on end-user environments (for example the environment of the domain expert).
5.1. Packaging and distributing the Visual Studio Code extension
The Visual Studio Code extension of a Langium DSL can be packaged as a .vsix file that can be published and copied/downloaded to end-users.
In the DSL project, we first move into the folder where the extension code is located:
cd packages/extension
Then we install the vsce dependency, which is required to build and package a Visual Studio Code extension:
npm install --save-dev @vscode/vsce
Finally we run vsce to build and package the extension:
npx vsce package
This produces a .vsix file that can be installed in any Visual Studio Code environment.
We implemented a DSL called the State Machines DSL using Langium. We would like to package and then distribute the Visual Studio Code extension of this DSL to end users, to allow them to use the editor to create State Machines programs.
In the project, we run the required command:
cd packages/extension
npm install --save-dev @vscode/vsce
npx vsce package
This produces a file packages/extension/vscode-state-machines-dsl-0.0.1.vsix.
Later, we take this file and copy it in the environment of an end-user (eg. a domain expert), and we install them in this new environment using this menu of the Extensions pane:
We can then see in the Extensions pane that the extension is correctly installed:
5.2. Packaging and distributing the CLI
The CLI (which usually includes the semantics) of a Langium DSL can be packaged and distributed as an npm package.
In the DSL project, we run this command:
npm pack -w packages/language -w packages/cli
which creates two files:
-
a
.tgzfor thelanguagepackage, which contains the DSL core components, -
a
.tgzfor theclipackage, which contains the DSL CLI.
These .tgz files can be copied into a user environment, and can then be installed with the following command:
npm install -g langium <language tgz> <cli tgz>
which installs langium libraries, the DSL core components .tgz, and the DSL CLI .tgz.
This installs a new command in the user environment, which can be directly executed from a terminal.
The name of this new command is the one specified in the bin part of the package.json of the cli package (see example below).
We implemented a DSL called the State Machines DSL using Langium. We would like to package and then distribute the CLI of this DSL to end users, to allow them to call the code generator on State Machines DSL programs.
In the root of the State Machines DSL project we run:
npm pack -w packages/language -w packages/cli
This produces the following two files:
-
state-machines-dsl-language-0.0.1.tgz: an npm package containing the core components of the DSL, -
state-machines-dsl-cli-0.0.1.tgz: an npm package containing the CLI of the DSL.
Later, we take these two files and copy them in the environment of an end-user (eg. a domain expert), and we install them in this new environment using this command:
npm install -g langium state-machines-dsl-cli-0.0.1.tgz state-machines-dsl-language-0.0.1.tgz
In this end-user environment, I can now call an executable called state-machines-dsl-cli to compile state machine programs, for example:
state-machines-dsl-cli generate my_state_machine.sm
We can find the name of the resulting executable (state-machines-dsl-cli) in the packages/cli/package.json of the State Machines DSL at this location:
|
"bin": {
"state-machines-dsl-cli": "./bin/cli.js"
},