Langium Simple Example: Creating a Basic DSL
Let’s say you want to create a simple DSL for defining greetings. Your DSL will allow users to write statements like:
greet "Alice"
greet "Bob"See repo ULL-ESIT-PL/learning-langium used to take the course Getting started with ‘Langium’
Step 1: Define the Grammar
First, you define the grammar for your language using Langium’s grammar language. This grammar describes the structure of valid statements in your DSL.
grammar Greetings
entry Model:
greetings+=Greeting*;
Greeting:
'greet' name=STRING;
hidden terminal WS: /\s+/;
terminal STRING: /"[^"]*"/;Modelis the root rule, which contains a list ofGreetingstatements.Greetingconsists of the keywordgreetfollowed by a string (the name).STRINGis a terminal rule for matching quoted strings.WSis a hidden rule for whitespace, which is ignored during parsing.
The meaning of greetings+=Greeting*;
Let us break the phrase greetings+=Greeting*; in detail:
greetings
This is the name of a property in the abstract syntax tree (AST). The AST is a tree representation of the structure of your program or DSL. Each rule in the grammar corresponds to a node in the AST, and properties like greetings define the relationships between nodes.
In this case, greetings is a property of the Model rule (the root rule in the grammar). It will hold a list of Greeting nodes.
+=
The += operator is used to append elements to a list. In Langium, this means that every time the parser encounters a Greeting statement, it will add it to the greetings list in the Model node.
For example, if your DSL input is:
greet "Alice"
greet "Bob"The greetings property in the Model node will contain a list with two Greeting nodes: one for "Alice" and one for "Bob".
Greeting
This refers to another rule in the grammar, specifically the Greeting rule. The Greeting rule defines the structure of a greeting statement in your DSL. In this example, the Greeting rule is defined as:
Greeting:
'greet' name=STRING;This means a Greeting consists of the keyword greet followed by a string (the name).
The * symbol
The * symbol is a cardinality operator. It means “zero or more.” In this context, it indicates that the Model can contain zero or more Greeting statements.
For example:
- If the input is empty,
greetingswill be an empty list. - If the input contains multiple
greetstatements,greetingswill contain all of them.
Putting It All Together
The full line greetings+=Greeting*; means:
- The
Modelnode has a property calledgreetings. - This property is a list that can hold zero or more
Greetingnodes. - Every time the parser encounters a
Greetingstatement, it will append it to thegreetingslist.
Example AST Representation
If your DSL input is:
greet "Alice"
greet "Bob"The resulting AST might look like this:
{
"$type": "Model",
"greetings": [
{
"$type": "Greeting",
"name": "Alice"
},
{
"$type": "Greeting",
"name": "Bob"
}
]
}- The
Modelnode has agreetingsproperty. - The
greetingsproperty is a list containing twoGreetingnodes. - Each
Greetingnode has anameproperty with the value"Alice"or"Bob".
Summary
greetingsis a property of theModelnode.+=appends elements to thegreetingslist.Greetingrefers to the rule defining a greeting statement.*means “zero or more” occurrences ofGreeting.
This line of grammar defines a flexible and reusable structure for your language, allowing you to handle multiple statements of the same type in a clean and organized way.
Step 2: Generate the Language Server
Langium provides tools to generate a language server from your grammar. The language server handles parsing, validation, and other language features.
-
Install Langium:
npm install langium -
Use the Langium CLI to generate the language server:
npx langium generate
This will generate TypeScript code for parsing and validating your DSL.
Step 3: Implement Custom Validation (Optional)
You can add custom validation logic. For example, let’s ensure that names in greetings are capitalized:
import { Greeting } from './generated/ast'; // Generated AST types
import { ValidationAcceptor } from 'langium';
export function validateGreeting(greeting: Greeting, accept: ValidationAcceptor) {
if (greeting.name.value[0] !== greeting.name.value[0].toUpperCase()) {
accept('warning', 'Name should be capitalized.', { node: greeting, property: 'name' });
}
}Step 4: Integrate with an Editor
Langium integrates with VS Code via the Language Server Protocol (LSP). You can package your language server as a VS Code extension to provide features like syntax highlighting, code completion, and validation.
-
Create a VS Code extension:
npx yo code -
Configure the extension to use your Langium language server.
-
Add syntax highlighting by defining a
language-configuration.jsonand atmLanguage.jsonfile for your DSL.
Step 5: Test Your DSL
Once everything is set up, you can open a .greet file in VS Code and write:
greet "alice"
greet "Bob"The editor will highlight syntax errors, warn you about uncapitalized names, and provide a smooth editing experience.