### Example Implementation File Source: https://langium.org/docs/recipes/multiple-languages This is an example of a '.hello' file, used for generating greetings. ```plaintext Hello Markus! Hello Michael! ``` -------------------------------- ### Example Configuration File Source: https://langium.org/docs/recipes/multiple-languages This is an example of a '.me' file, used for configuration details. ```plaintext I am Markus. ``` -------------------------------- ### Install esbuild Source: https://langium.org/docs/recipes/code-bundling Install esbuild as a development dependency for bundling your code. ```bash npm i --save-dev esbuild ``` -------------------------------- ### Example Definition File Source: https://langium.org/docs/recipes/multiple-languages This is an example of a '.who' file, used for defining entities. ```plaintext person Markus person Michael person Frank ``` -------------------------------- ### Full Domain Model Formatter Example Source: https://langium.org/docs/recipes/formatting A comprehensive example demonstrating formatting for entities, package declarations, and the root domain model. ```typescript import { AstNode } from 'langium'; import { AbstractFormatter, Formatting } from 'langium/lsp'; import * as ast from './generated/ast.js'; export class DomainModelFormatter extends AbstractFormatter { protected format(node: AstNode): void { if (ast.isEntity(node) || ast.isPackageDeclaration(node)) { const formatter = this.getNodeFormatter(node); const bracesOpen = formatter.keyword('{'); const bracesClose = formatter.keyword('}'); formatter.interior(bracesOpen, bracesClose).prepend(Formatting.indent()); bracesClose.prepend(Formatting.newLine()); formatter.property('name').surround(Formatting.oneSpace()); } else if (ast.isDomainmodel(node)) { const formatter = this.getNodeFormatter(node); ``` -------------------------------- ### Langium Grammar Example with Multi-Target Cross-References Source: https://langium.org/docs/reference/grammar-language This example demonstrates how a Greeting object can reference multiple Person objects with the same name. ```text person Bob person Bob Hello Bob ! ``` -------------------------------- ### Example .hello File Content Source: https://langium.org/docs/recipes/performance/caches This is an example of a .hello file content used for testing the functionality, including persons with known and unknown publishers. ```plaintext person Wonderwoman person Spiderman person Homer //warning: unknown publisher!! person Obelix ``` -------------------------------- ### Install esbuild for Extension Bundling Source: https://langium.org/docs/learn/minilogo/building_an_extension Install esbuild as a development dependency to bundle your extension code. ```bash npm i --save-dev esbuild ``` -------------------------------- ### Complete Langium Grammar Example Source: https://langium.org/docs/learn/workflow/write_grammar The complete grammar for the Hello-World example, defining terminals and parser rules for models, persons, and greetings. ```langium grammar HelloWorld hidden terminal WS: /\s+/; terminal ID: /[_a-zA-Z][\w]*/; entry Model: (persons+=Person | greetings+=Greeting)*; Person: 'person' name=ID; Greeting: 'Hello' person=[Person] '!'; ``` -------------------------------- ### Parse and Validate MiniLogo Program (Subdirectory) Source: https://langium.org/docs/learn/minilogo/customizing_cli Example of using the CLI to parse and validate a MiniLogo program located in an 'examples' subdirectory. ```bash ./bin/cli parseAndValidate examples/test.logo ``` -------------------------------- ### Langium Language Example Source: https://langium.org/docs/learn/workflow/scaffold This is a sample of a Langium language definition. It defines 'person' and 'Hello' entities. Use this as a starting point for your DSL. ```plaintext person Alice Hello Alice! person Bob Hello Bob! ``` -------------------------------- ### Example .hello file for Dependency Resolution Source: https://langium.org/docs/recipes/validation/dependency-loops A '.hello' file illustrating dependency resolution through topological sort. It shows a scenario where greetings are ordered based on prerequisites. ```plaintext person Homer person Marge person Pinky person Brain Homer greets Marge! Brain greets Pinky! Pinky greets Marge! ``` -------------------------------- ### Example .hello file with Greeting Loops Source: https://langium.org/docs/recipes/validation/dependency-loops A sample '.hello' file demonstrating greeting loops, which will trigger validation errors. Ensure to build the project after implementing the validator. ```plaintext person Homer person Marge person Pinky person Brain Homer greets Marge! //error Marge greets Brain! //error Brain greets Homer! //error Pinky greets Marge! ``` -------------------------------- ### Langium Grammar Example with Unordered Group Source: https://langium.org/docs/reference/grammar-language This example shows how an unordered group allows properties 'name' and 'age' to be declared in any order. ```text person 25 Bob ``` -------------------------------- ### MiniLogo Example Program Source: https://langium.org/docs/learn/minilogo/generation_in_the_web This is an example program written in the MiniLogo language. It defines a function `test` to draw a diamond shape and then calls it after setting the color to white. ```minilogo def test() { move(100, 0) pen(down) move(100, 100) move(-100, 100) move(-100, -100) move(100, -100) pen(up) } color(white) test() ``` -------------------------------- ### Example Expressions with Infix Operators Source: https://langium.org/docs/reference/grammar-language/infix-operators These examples illustrate common grammar ambiguities that arise with infix operators like addition, multiplication, and subtraction. ```plaintext a + b * c a - b - c ``` -------------------------------- ### Define an Entry Rule for Parsing Start Source: https://langium.org/docs/reference/grammar-language Defines the starting point of the parsing process with the 'entry' keyword. This rule 'Model' parses a sequence of 'Person' or 'Greeting' objects. ```langium entry Model: (persons+=Person | greetings+=Greeting)*; ``` -------------------------------- ### MiniLogo Example Program Source: https://langium.org/docs/learn/minilogo/customizing_cli A simple program written in the MiniLogo language to test the CLI's parsing and validation capabilities. Save this as a .logo file. ```minilogo def test() { pen(down) move(10,10) pen(up) } test() ``` -------------------------------- ### Install Yeoman and Langium Generator Source: https://langium.org/docs/learn/workflow/install Installs Yeoman and the Langium extension generator globally using npm. Ensure you have Node.js version 16 or higher. ```bash npm i -g yo generator-langium ``` -------------------------------- ### Get Language Client and Listen for Notifications Source: https://langium.org/docs/learn/minilogo/generation_in_the_web Retrieve the MonacoLanguageClient instance and set up a listener for 'browser/DocumentChange' notifications. Ensure the wrapper has started successfully before calling getLanguageClient. ```typescript // wrapper has started... // get the language client const client = wrapper.getLanguageClient(); if (!client) { throw new Error('Unable to obtain language client!'); } // listen for document change notifications client.onNotification('browser/DocumentChange', onDocumentChange); function onDocumentChange(resp: any) { let commands = JSON.parse(resp.content).$commands; // ... do something with these commands } ``` -------------------------------- ### Dependency Resolution Example Source: https://langium.org/docs/recipes/validation/dependency-loops A simple representation of package imports or function call dependencies showing a loop-free order. ```plaintext A -> B -> C A -> C C -> D //resolution: A, B, C, D ``` -------------------------------- ### Example Langium Language Input Source: https://langium.org/docs/learn/workflow/resolve_cross_references This is an example of input text for a Langium language, demonstrating the use of cross-references like 'John' and 'Jane'. ```plaintext person John person Jane Hello John! Hello Jane! ``` -------------------------------- ### Invalid Hello-World Input Example Source: https://langium.org/docs/learn/workflow/create_validations This input file is invalid because John is greeted twice, violating the rule that each person should be greeted at most once. ```plaintext person John person Jane Hello John! Hello Jane! Hello John! //should throw: You can great each person at most once! This is the 2nd greeting to John. ``` -------------------------------- ### Package.json Scripts for Monaco Setup Source: https://langium.org/docs/learn/minilogo/langium_and_monaco These scripts in package.json automate the preparation of public assets and the building of the web application, including copying Monaco editor assets. ```json { ... "prepare:public": "node scripts/prepare-public.mjs", "build:web": "npm run build && npm run prepare:public && npm run build:worker && node scripts/copy-monaco-assets.mjs", } ``` -------------------------------- ### Example Python-like Code Source: https://langium.org/docs/recipes/lexing/indentation-sensitive-languages This is a sample code snippet that conforms to the PythonIf grammar, demonstrating nested if-else statements and return values. ```plaintext if true: return false else: if true: return true ``` -------------------------------- ### Import Grammar Rules Source: https://langium.org/docs/reference/grammar-language Reuse grammar rules from other .langium files by importing them. This example imports rules from './path/to/an/other/langium/grammar'. ```langium import './path/to/an/other/langium/grammar'; ``` -------------------------------- ### Original Langium Grammar Source: https://langium.org/docs/recipes/multiple-languages The initial grammar for the 'hello-world' example before splitting into multiple files. It defines 'Person' and 'Greeting' rules. ```langium grammar MultipleLanguages entry Model: (persons+=Person | greetings+=Greeting)*; Person: 'person' name=ID; Greeting: 'Hello' person=[Person:ID] '!'; hidden terminal WS: /\s+/; terminal ID: /[_a-zA-Z][\w_]*/; terminal INT returns number: /[0-9]+/; terminal STRING: /"(\\.|[^"\\])*"|'(\\.|[^'\\])*'/; hidden terminal ML_COMMENT: /\/\*[\s\S]*?\*\//; hidden terminal SL_COMMENT: /\/\/[^\n\r]*/; ``` -------------------------------- ### Open Langium Project in VS Code Source: https://langium.org/docs/learn/workflow/scaffold After the project is generated and dependencies are installed, use this command to open the project folder in VS Code. Replace 'hello-world' with your chosen project name. ```bash code hello-world ``` -------------------------------- ### Type Inference Example in Lox Source: https://langium.org/docs/recipes/scoping/class-member Illustrates a typical scenario of class instantiation and member access for type inference. This example is part of the Lox implementation. ```typescript class Container { sub: SubContainer } class SubContainer { name: string } // Constructor call var element = Container(); // Member access println(element.sub.name); ``` -------------------------------- ### Add Serve Script to package.json Source: https://langium.org/docs/learn/minilogo/langium_and_monaco A package.json script entry to easily start the NodeJS server for serving built web assets. Assumes the compiled server code is located at './out/web/app.js'. ```json { "...": "...", "serve": "node ./out/web/app.js" } ``` -------------------------------- ### Initialize Monaco Editor Language Client Wrapper Source: https://langium.org/docs/learn/minilogo/langium_and_monaco Demonstrates how to create and start the MonacoEditorLanguageClientWrapper with a generated UserConfig. Ensure the HTML element, language ID, code, worker, and Monarch grammar are correctly provided. ```typescript // create a wrapper instance const wrapper = new MonacoEditorLanguageClientWrapper(); // start up with a user config await wrapper.start(createUserConfig({ htmlElement: document.getElementById("monaco-editor-root")!, languageId: 'minilogo', code: getMainCode(), worker: getWorker(), monarchGrammar: getMonarchGrammar() })); ``` -------------------------------- ### Class Member Scoping Example Source: https://langium.org/docs/recipes/scoping/class-member Demonstrates member-based scoping in a TypeScript-like syntax, showing how to access members of nested classes. ```typescript class A { b: B; } class B { value: string; } function test(): void { const a = new A(); const b = a.b; // Refers to the `b` defined in class `A` const value = b.value; // Refers to the `value` defined in class `B` } ``` -------------------------------- ### Install Graphology for Validations Source: https://langium.org/docs/recipes/validation/dependency-loops Install the necessary graph libraries for implementing advanced validations like cycle detection. These packages provide data structures and algorithms for graph manipulation. ```bash npm install graphology graphology-components graphology-dag ``` -------------------------------- ### C++ Namespace Example for Multi-target References Source: https://langium.org/docs/learn/workflow/resolve_cross_references Demonstrates how to use namespaces to group related declarations across multiple files, enabling multi-target references. This pattern is useful for organizing code and avoiding naming conflicts. ```cpp //vector.hpp namespace std { class vector { ... }; } //map.hpp namespace std { class map { ... }; } //main.cpp #include #include std::vector v; std::map m; ... ``` -------------------------------- ### Implement Custom Services in ArithmeticsModule Source: https://langium.org/docs/reference/configuration-services Specify the implementation for your custom services within the ArithmeticsModule. This example registers the ArithmeticsValidator service. ```typescript export const ArithmeticsModule: Module = { validation: { ArithmeticsValidator: () => new ArithmeticsValidator() } }; ``` -------------------------------- ### Lexer Output for Example Code Source: https://langium.org/docs/recipes/lexing/indentation-sensitive-languages The sequence of tokens generated by the lexer for the provided Python-like code sample, illustrating how terminals are recognized. ```plaintext if, BOOLEAN, INDENT, return, BOOLEAN, DEDENT, else, INDENT, if, BOOLEAN, INDENT, return, BOOLEAN, DEDENT, DEDENT ``` -------------------------------- ### Function Call Graph Resolution Source: https://langium.org/docs/recipes/validation/dependency-loops Example of a function call graph demonstrating the order of compilation or execution respecting dependencies. ```c void answer42() { printf("42\n"); } void bar1() { answer42(); } void foo() { bar1(); bar2(); } void bar2() { answer42(); } ``` -------------------------------- ### Setup Monaco Workers and Styles Source: https://langium.org/docs/learn/minilogo/langium_and_monaco This TypeScript function initializes Monaco's workers and applies necessary styles for the editor. It requires the `monaco-editor-wrapper` and `monaco-editor-workers` packages. ```typescript import { MonacoEditorLanguageClientWrapper, UserConfig } from "monaco-editor-wrapper/bundle"; import { buildWorkerDefinition } from "monaco-editor-workers"; import { addMonacoStyles } from 'monaco-editor-wrapper/styles'; /** * Setup Monaco's own workers and also incorporate the necessary styles for the monaco-editor */ function setup() { buildWorkerDefinition( './monaco-editor-workers/workers', new URL('', window.location.href).href, false ); addMonacoStyles('monaco-editor-styles'); } ``` -------------------------------- ### Run Yeoman Generator for Langium Source: https://langium.org/docs/learn/workflow/scaffold Execute this command to start the Yeoman generator for Langium. It will prompt you for project details like extension name, language name, and file extensions. ```bash > yo langium ┌─────┐ ─┐ ┌───┐ │ ╶─╮ ┌─╮ ╭─╮ ╷ ╷ ╷ ┌─┬─╮ │ ,´ │ ╭─┤ │ │ │ │ │ │ │ │ │ │╱ ╰─ ╰─┘ ╵ ╵ ╰─┤ ╵ ╰─╯ ╵ ╵ ╵ ` ╶─╯ Welcome to Langium! This tool generates a VS Code extension with a "Hello World" language to get started quickly. The extension name is an identifier used in the extension marketplace or package registry. ❓ Your extension name: hello-world The language name is used to identify your language in VS Code. Please provide a name to be shown in the UI. CamelCase and kebab-case variants will be created and used in different parts of the extension and language server. ❓ Your language name: Hello World Source files of your language are identified by their file name extension. You can specify multiple file extensions separated by commas. ❓ File extensions: .hello Your language can be run inside of a VSCode extension. ❓ Include VSCode extension? Yes You can add CLI to your language. ❓ Include CLI? Yes You can run the language server in your web browser. ❓ Include Web worker? Yes You can add the setup for language tests using Vitest. ❓ Include language tests? Yes ``` -------------------------------- ### Test Langium Parser with AST Comparison Source: https://langium.org/docs/learn/workflow/generate_ast This example demonstrates how to test the Langium parser by creating language services, parsing an input string, and asserting the structure of the generated AST. Note that cross-references are not resolved at this stage. ```typescript import { createHelloWorldServices } from "./your-project//hello-world-module.js"; import { EmptyFileSystem } from "langium"; import { parseHelper } from "langium/test"; import { Model } from "../../src/language/generated/ast.js"; //arrange const services = createHelloWorldServices(EmptyFileSystem); const parse = parseHelper(services.HelloWorld); //act const document = await parse(` person John person Jane Hello John! Hello Jane! `); //assert const model = document.parseResult.value; expect(model.persons).toHaveLength(2); expect(model.persons[0].name).toBe("John"); expect(model.persons[1].name).toBe("Jane"); expect(model.greetings).toHaveLength(2); //be aware of the fact that the following checks will fail at this point, because the cross-references are not resolved yet expect(model.greetings[0].person.ref?.name).toBe("John"); expect(model.greetings[1].person.ref?.name).toBe("Jane"); ``` -------------------------------- ### Organize Custom Services with Groups Source: https://langium.org/docs/reference/configuration-services Nest new services within properties to create logical groups for better organization. This example demonstrates nested groups for validation and other services. ```typescript export type ArithmeticsAddedServices = { validation: { ArithmeticsValidator: ArithmeticsValidator }, secondGroup: { AnotherServiceName: AnotherServiceType }, nthGroup: { withASubGroup: { YetAnotherServiceName: YetAnotherServiceType } } } ``` -------------------------------- ### Example of Qualified Name Scoping in C++ Source: https://langium.org/docs/recipes/scoping/qualified-name Demonstrates how to call a function within a namespace using its fully qualified name. This is useful when the function is not directly accessible in the current scope. ```cpp namespace Langium { void getDocumentation(); } void main() { // Should call the `getDocumentation` function defined in the `Langium` namespace Langium::getDocumentation(); } ``` -------------------------------- ### C Function Call Recursion Loop Source: https://langium.org/docs/recipes/validation/dependency-loops Demonstrates a C code example where function calls create a recursion loop, leading to potential stack overflow. ```c void foo() { bar(); } void bar() { answer42(); } void answer42() { bar(); //error, foo calls bar, bar calls answer42, answer42 calls foo } ``` -------------------------------- ### VSCode API Editor App Config Source: https://langium.org/docs/learn/minilogo/langium_and_monaco Example of an editor app configuration using the 'vscodeApi' type, suitable for TextMate grammars instead of Monarch grammars. ```typescript editorAppConfig: { $type: 'vscodeApi', languageId: id, useDiffEditor: false, code: config.code, ... } ``` -------------------------------- ### Create Langium Services with Custom Module Source: https://langium.org/docs/recipes/builtin-library Creates the Langium shared and language-specific services, injecting the custom `HelloWorldSharedModule`. This ensures the custom workspace manager is part of the service setup. ```typescript export function createHellowWorldServices(context: DefaultSharedModuleContext): { shared: LangiumSharedServices, services: HelloWordServices } { const shared = inject( createDefaultSharedModule(context), HelloWorldGeneratedSharedModule, HelloWorldSharedModule ); const services = inject( createDefaultModule({ shared }), HelloWorldGeneratedModule, HelloWorldModule ); shared.ServiceRegistry.register(services); return { shared, services }; } ``` -------------------------------- ### Declare Language Name Source: https://langium.org/docs/reference/grammar-language An entry grammar file must start with a header declaring the language name. This example declares a language named 'MyLanguage'. ```langium grammar MyLanguage ``` -------------------------------- ### Build and Serve MiniLogo Project Source: https://langium.org/docs/learn/minilogo/generation_in_the_web Run these commands to build the web version of your MiniLogo project and serve it locally. Ensure the generator script is also executed by `build:web`. ```bash npm run build:web npm run serve ``` -------------------------------- ### Class and Method Scope Example in TypeScript Source: https://langium.org/docs/features Demonstrates scoping within classes and methods in TypeScript. It shows how symbols like class names and method names are resolved within their respective scopes, similar to Langium's scoping mechanisms. ```typescript class X { y(): void { ... } } const instance = new X(); // Symbol `X` is in the local scope instance.y(); // Symbol `y` exists in the scope of the `X` class ``` -------------------------------- ### Browser Entry Point for Langium Language Server Source: https://langium.org/docs/learn/minilogo/langium_and_monaco Sets up a new entry point for the language server to be used in a browser context. It uses `EmptyFileSystem` and browser-specific message readers/writers. ```typescript import { startLanguageServer, EmptyFileSystem } from 'langium'; import { BrowserMessageReader, BrowserMessageWriter, createConnection } from 'vscode-languageserver/browser.js'; // your services & module name may differ based on your language's name import { createMiniLogoServices } from './minilogo-module.js'; declare const self: DedicatedWorkerGlobalScope; /* browser specific setup code */ const messageReader = new BrowserMessageReader(self); const messageWriter = new BrowserMessageWriter(self); const connection = createConnection(messageReader, messageWriter); // Inject the shared services and language-specific services const { shared, MiniLogo } = createMiniLogoServices({connection, ...EmptyFileSystem }); // Start the language server with the shared services startLanguageServer(shared); ``` -------------------------------- ### Build and Generate Commands Source: https://langium.org/docs/learn/minilogo/generation Commands to build the project and generate MiniLogo commands from a test file. ```bash npm run build ./bin/cli generate test.logo ``` -------------------------------- ### Build and Generate Langium CLI Source: https://langium.org/docs/learn/minilogo/customizing_cli Run these commands from the project root to build the Langium CLI and generate necessary files. Ensure your Langium configuration is up-to-date. ```bash npm run langium:generate npm run build ``` -------------------------------- ### Initial Langium Configuration Source: https://langium.org/docs/recipes/multiple-languages This is the initial configuration for a single language project. It defines the project name, language ID, grammar file, file extensions, and output paths for TextMate and Monarch syntaxes. ```json { "projectName": "MultipleLanguages", "languages": [{ "id": "multiple-languages", "grammar": "src/language/multiple-languages.langium", "fileExtensions": [".hello"], "textMate": { "out": "syntaxes/multiple-languages.tmLanguage.json" }, "monarch": { "out": "syntaxes/multiple-languages.monarch.ts" } }], "out": "src/language/generated" } ``` -------------------------------- ### Generated JSON Output Source: https://langium.org/docs/learn/minilogo/generation The JSON output produced by the generator for the example MiniLogo program. ```json [ { "cmd": "penDown" }, { "cmd": "move", "x": 10, "y": 10 }, { "cmd": "penUp" } ] ``` -------------------------------- ### Set up onBuildPhase Listener for Document Validation Source: https://langium.org/docs/learn/minilogo/generation_in_the_web Add this code to the end of the `startLanguageServer` function in `main-browser.ts`. It listens for fully validated documents and sends a notification with the serialized AST and generated commands. ```typescript // modified import from the previous tutorial: Langium + Monaco import { BrowserMessageReader, BrowserMessageWriter, Diagnostic, NotificationType, createConnection } from 'vscode-languageserver/browser.js'; // additional imports import { Model } from './generated/ast.js'; import { Command, getCommands } from './minilogo-actions.js'; import { generateStatements } from '../generator/generator.js'; // startLanguageServer... // Send a notification with the serialized AST after every document change type DocumentChange = { uri: string, content: string, diagnostics: Diagnostic[] }; const documentChangeNotification = new NotificationType('browser/DocumentChange'); // use the built-in AST serializer const jsonSerializer = MiniLogo.serializer.JsonSerializer; // listen on fully validated documents shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.Validated, documents => { // perform this for every validated document in this build phase batch for (const document of documents) { const model = document.parseResult.value as Model; let json: Command[] = []; // only generate commands if there are no errors if(document.diagnostics === undefined || document.diagnostics.filter((i) => i.severity === 1).length === 0 ) { json = generateStatements(model.stmts); } // inject the commands into the model // this is safe so long as you careful to not clobber existing properties // and is incredibly helpful to enrich the feedback you get from the LS per document (model as unknown as {$commands: Command[]}).$commands = json; // send the notification for this validated document, // with the serialized AST + generated commands as the content connection.sendNotification(documentChangeNotification, { uri: document.uri.toString(), content: jsonSerializer.serialize(model, { sourceText: true, textRegions: true }), diagnostics: document.diagnostics ?? [] }); } }); ``` -------------------------------- ### Define the Person Rule Source: https://langium.org/docs/learn/workflow/write_grammar Define the 'Person' rule, which starts with the 'person' keyword and assigns an 'ID' to the 'name' property. ```langium Person: 'person' name=ID; ``` -------------------------------- ### Create Express Server for Static Assets Source: https://langium.org/docs/learn/minilogo/langium_and_monaco An alternative, more concise server implementation using the Express framework for serving static files. Requires 'express' and '@types/express' dependencies. ```typescript /** * Simple express app for serving generated examples */ import express from 'express'; const app = express(); const port = 3000; app.use(express.static('./public')); app.listen(port, () => { console.log(`Server for MiniLogo assets listening on http://localhost:${port}`); }); ``` -------------------------------- ### Define the Greeting Rule with Cross-Reference Source: https://langium.org/docs/learn/workflow/write_grammar Define the 'Greeting' rule, starting with 'Hello' and including a cross-reference to a 'Person' by its 'name' property. ```langium Greeting: 'Hello' person=[Person] '!'; ``` -------------------------------- ### Implementation Grammar with Import Source: https://langium.org/docs/recipes/multiple-languages The grammar for implementing greetings, which imports the 'Person' definition from the definition grammar. It defines the 'Greeting' rule. ```langium grammar MultiImplementation import "multiple-languages-definition"; entry ImplementationUnit: (greetings+=Greeting)*; Greeting: 'Hello' person=[Person:ID] '!'; ``` -------------------------------- ### Cardinality and Interruption Source: https://langium.org/docs/reference/grammar-language When an assignment has a cardinality of '+' or '*', the expressions must be contiguous. This example shows a 'Paragraph' rule where 'sentences' must not be interrupted. ```langium Paragraph: 'paragraph' (sentences+=STRING)+ id=INT; ``` -------------------------------- ### Get Canvas and Context Source: https://langium.org/docs/learn/minilogo/generation_in_the_web Obtains a reference to the HTML canvas element and its 2D rendering context. Throws an error if either cannot be found. ```typescript const canvas : HTMLCanvasElement | null = document.getElementById('minilogo-canvas') as HTMLCanvasElement | null; if (!canvas) { throw new Error('Unable to find canvas element!'); } const context = canvas.getContext('2d'); if (!context) { throw new Error('Unable to get canvas context!'); } ``` -------------------------------- ### Define Custom Services in ArithmeticsAddedServices Source: https://langium.org/docs/reference/configuration-services Declare application-specific services as properties within the ArithmeticsAddedServices type. This example shows a simple validator service. ```typescript export type ArithmeticsAddedServices = { ArithmeticsValidator: ArithmeticsValidator } ``` -------------------------------- ### Generate VSIX Extension using vsce Source: https://langium.org/docs/learn/minilogo/building_an_extension Use the VS Code Extension Manager (vsce) to package your project into a VSIX file for distribution and installation. ```bash vsce package ``` -------------------------------- ### Declare MyType Interface with Property Mismatch Source: https://langium.org/docs/reference/semantic-model This example demonstrates a validation error when a parser rule attempts to include a property not defined in the declared interface. ```langium Z returns MyType: name=ID age=INT; ``` -------------------------------- ### Register HelloWorld Services in Module Source: https://langium.org/docs/recipes/performance/caches Register custom services like InferPublisherService and HelloWorldValidator within the HelloWorldModule. Ensure the 'services' parameter is correctly passed to the validator. ```typescript export type HelloWorldAddedServices = { utilities: { inferPublisherService: InferPublisherService }, validation: { HelloWorldValidator: HelloWorldValidator } } //... export const HelloWorldModule: Module = { utilities: { inferPublisherService: (services) => new CachedInferPublisherService(services) }, validation: { //add `services` parameter here HelloWorldValidator: (services) => new HelloWorldValidator(services) } }; ``` -------------------------------- ### Define Builtin Library Content Source: https://langium.org/docs/recipes/builtin-library Defines the source code for a builtin library using a template literal. This example uses the 'hello world' language. ```typescript export const builtinHelloWorld = ` person Jane person John `.trimLeft(); ``` -------------------------------- ### Set Activation Events Source: https://langium.org/docs/recipes/builtin-library Sets the 'activationEvents' in package.json to 'onFileSystem:builtin'. This ensures the extension activates when any file with the 'builtin' scheme is opened, even before a DSL file is opened. ```json "activationEvents": [ "onFileSystem:builtin" ] ``` -------------------------------- ### Implement Custom FileSystemProvider for Builtin Libraries Source: https://langium.org/docs/recipes/builtin-library Implements a `FileSystemProvider` for the 'builtin' URI scheme to allow VS Code to read builtin library files. This provider handles `stat` and `readFile` operations for the in-memory library. ```typescript import * as vscode from 'vscode'; import { builtinHelloWorld } from './language/builtins'; export class DslLibraryFileSystemProvider implements vscode.FileSystemProvider { static register(context: vscode.ExtensionContext) { context.subscriptions.push( vscode.workspace.registerFileSystemProvider('builtin', new DslLibraryFileSystemProvider(context), { isReadonly: true, isCaseSensitive: false })); } stat(uri: vscode.Uri): vscode.FileStat { const date = Date.now(); return { ctime: date, mtime: date, size: Buffer.from(builtinHelloWorld).length, type: vscode.FileType.File }; } readFile(uri: vscode.Uri): Uint8Array { // We could return different libraries based on the URI // We have only one, so we always return the same return new Uint8Array(Buffer.from(builtinHelloWorld)); } // The following class members only serve to satisfy the interface private readonly didChangeFile = new vscode.EventEmitter(); onDidChangeFile = this.didChangeFile.event; watch() { return { dispose: () => {} }; } readDirectory(): [] { ``` -------------------------------- ### Import and greet persons Source: https://langium.org/docs/recipes/scoping/file-based Imports persons from another module and attempts to greet them. Demonstrates handling of valid imports, aliasing, and references to non-exported or overwritten names. ```langium import { Marge, Homer, Lisa, //reference error, because not exported Maggy as Baby } from "persons.hello" Hello Lisa! //reference error, because no valid import Hello Maggy! //reference error, because name was overwritten with 'Baby' Hello Homer! Hello Marge! Hello Baby! ``` -------------------------------- ### Lexical Scoping Example in TypeScript Source: https://langium.org/docs/recipes/scoping Demonstrates how variable availability is determined by the scope (e.g., blocks) in which they are declared. Variables declared within a block are not accessible outside of it. ```typescript let x = 42; x = 3; // References the `x` defined in the previous line if (condition) { let y = 42; } y = 3; // Cannot link, `y` isn't in any of the available scopes ``` -------------------------------- ### npm script for esbuild Source: https://langium.org/docs/recipes/code-bundling Add a build script to your package.json to easily run the esbuild bundling process. ```json "scripts": { "build": "node ./esbuild.mjs" } ``` -------------------------------- ### Prepare Public Assets Script (esbuild) Source: https://langium.org/docs/learn/minilogo/langium_and_monaco This script uses esbuild to bundle and minify TypeScript files for static assets and copies them along with CSS and HTML files to the public directory. ```javascript import * as esbuild from 'esbuild' import shell from 'shelljs' // setup & copy over css & html to public shell.mkdir('-p', './public'); shell.cp('-fr', './src/static/*.css', './public/'); shell.cp('-fr', './src/static/*.html', './public'); // bundle minilogo.ts, and also copy to public await esbuild.build({ entryPoints: ['./src/static/minilogo.ts'], minify: true, sourcemap: true, bundle: true, outfile: './public/minilogo.js', }); ``` -------------------------------- ### Create NodeJS HTTP Server for Static Assets Source: https://langium.org/docs/learn/minilogo/langium_and_monaco A basic NodeJS server implementation for serving static files from a 'public' directory. It handles MIME types and basic routing, redirecting all not-found requests to index.html. ```typescript /** * Simple server app for serving generated examples locally * Based on: https://developer.mozilla.org/en-US/docs/Learn/Server-side/Node_server_without_framework */ import * as fs from "node:fs"; import * as http from "node:http"; import * as path from "node:path"; const port = 3000; const MIME_TYPES: Record = { default: "application/octet-stream", html: "text/html; charset=UTF-8", js: "application/javascript", css: "text/css", }; const STATIC_PATH = path.join(process.cwd(), "./public"); const toBool = [() => true, () => false]; const prepareFile = async (url: string) => { const paths = [STATIC_PATH, url]; if (url.endsWith("/")) { paths.push("index.html"); } const filePath = path.join(...paths); const pathTraversal = !filePath.startsWith(STATIC_PATH); const exists = await fs.promises.access(filePath).then(...toBool); const found = !pathTraversal && exists; // there's no 404, just redirect to index.html in all other cases const streamPath = found ? filePath : STATIC_PATH + "/index.html"; const ext = path.extname(streamPath).substring(1).toLowerCase(); const stream = fs.createReadStream(streamPath); return { found, ext, stream }; }; http .createServer(async (req, res) => { const file = await prepareFile(req.url!); const statusCode = file.found ? 200 : 404; const mimeType: string = MIME_TYPES[file.ext] || MIME_TYPES.default; res.writeHead(statusCode, { "Content-Type": mimeType }); file.stream.pipe(res); console.log(`${req.method} ${req.url} ${statusCode}`); }) .listen(port); console.log(`Server for MiniLogo assets listening on http://localhost:${port}`); ``` -------------------------------- ### Define NONO Terminal with EBNF Negated Token Source: https://langium.org/docs/reference/grammar-language Defines a NONO terminal rule that matches a word not starting with 'no'. This uses the '!' operator for a negative lookahead in EBNF. ```langium terminal NONO: (!'no')('a'..'z'|'A'..'Z')+; ``` -------------------------------- ### Evaluate For Loop Statement Source: https://langium.org/docs/learn/minilogo/generation Executes a for loop by evaluating the start and end bounds. A new environment is copied for each iteration to avoid altering the original environment. Results are accumulated. ```typescript // compute for loop bounds // start let vi = evalExprWithEnv(stmt.e1, env); // end let ve = evalExprWithEnv(stmt.e2, env); let results : (Object | undefined)[] = []; // perform loop const loopEnv = new Map(env); while(vi < ve) { loopEnv.set(stmt.var.name, vi++); stmt.body.forEach(s => { results = results.concat(evalStmt(s, new Map(loopEnv))); }); } return results; ``` -------------------------------- ### Define For Loop Syntax in Langium Source: https://langium.org/docs/learn/minilogo/writing_a_grammar Defines a parser rule for a simple 'For' loop. It includes a parameter for the loop variable, start and end expressions, and a block of statements. ```langium For: 'for' var=Param '=' e1=Expr 'to' e2=Expr Block; ``` -------------------------------- ### Handle Left Recursion with Tree-Rewriting Actions Source: https://langium.org/docs/reference/grammar-language Use tree-rewriting actions to clean the AST and avoid issues with left-recursive grammar rules. This example shows how to rewrite the Addition rule. ```langium Addition: SimpleExpression ({Addition.left=current} '+' right=SimpleExpression)*; SimpleExpression: '(' Addition ')' | value=INT; ``` -------------------------------- ### Define the Entry Model Rule Source: https://langium.org/docs/learn/workflow/write_grammar Define the 'Model' as the entry point for the grammar. It allows for repeating groups of 'Person' or 'Greeting' elements. ```langium entry Model: (persons+=Person | greetings+=Greeting)*; ``` -------------------------------- ### Adapt HelloWorld Grammar for Greetings Source: https://langium.org/docs/recipes/validation/dependency-loops Modify the 'HelloWorld' grammar to include 'Person' and 'Greeting' rules, enabling interactions between entities. Ensure to rebuild the grammar after changes. ```langium grammar HelloWorld entry Model: (persons+=Person | greetings+=Greeting)*; Person: 'person' name=ID; Greeting: greeter=[Person:ID] 'greets' greeted=[Person:ID] '!'; hidden terminal WS: /\s+/; terminal ID: /[_a-zA-Z][\w_]*/; ``` -------------------------------- ### Full Implementation of CppScopeComputation Source: https://langium.org/docs/recipes/scoping/qualified-name This implementation exports all functions using their fully qualified name and collects local symbols. It utilizes `streamAllContents` to iterate through document contents and `descriptions.createDescription` to generate node descriptions. ```typescript export class CppScopeComputation extends DefaultScopeComputation { /** * Export all functions using their fully qualified name */ override async collectExportedSymbols(document: LangiumDocument): Promise { const exportedDescriptions: AstNodeDescription[] = []; for (const childNode of streamAllContents(document.parseResult.value)) { if (isFunctionDeclaration(childNode)) { const fullyQualifiedName = this.getQualifiedName(childNode, childNode.name); // `descriptions` is our `AstNodeDescriptionProvider` defined in `DefaultScopeComputation` // It allows us to easily create descriptions that point to elements using a name. exportedDescriptions.push(this.descriptions.createDescription(childNode, fullyQualifiedName, document)); } } return exportedDescriptions; } override async collectLocalSymbols(document: LangiumDocument): Promise { const model = document.parseResult.value as CppProgram; // This multi-map stores a list of descriptions for each node in our document const scopes = new MultiMap(); this.processContainer(model, scopes, document); return scopes; } private processContainer( container: CppProgram | Namespace, scopes: PrecomputedScopes, document: LangiumDocument ): AstNodeDescription[] { const localDescriptions: AstNodeDescription[] = []; for (const element of container.elements) { if (isFunctionDeclaration(element)) { // Create a simple local name for the function const description = this.descriptions.createDescription(element, element.name, document); localDescriptions.push(description); } else if (isNamespace(element)) { const nestedDescriptions = this.processContainer(element, scopes, document); for (const description of nestedDescriptions) { // Add qualified names to the container // This could also be a partially qualified name ``` -------------------------------- ### Associating Validation Function with Node Type Source: https://langium.org/docs/learn/minilogo/validation This example shows how to map a specific AST node type ('Person') to a validation function ('checkPersonStartsWithCapital') within the validation checks object. ```typescript Person: validator.checkPersonStartsWithCapital ``` -------------------------------- ### esbuild Configuration for Langium Source: https://langium.org/docs/recipes/code-bundling A minimal esbuild configuration to bundle Langium language servers and extensions. It supports watch mode and minification. ```typescript //@ts-check import * as esbuild from 'esbuild'; const watch = process.argv.includes('--watch'); const minify = process.argv.includes('--minify'); const ctx = await esbuild.context({ entryPoints: ['src/extension.ts', 'src/language/main.ts'], outdir: 'out', bundle: true, target: "es6", loader: { '.ts': 'ts' }, external: ['vscode'], // the vscode-module is created on-the-fly and must be excluded. platform: 'node', // VSCode extensions run in a node process sourcemap: !minify, minify }); if (watch) { await ctx.watch(); } else { await ctx.rebuild(); ctx.dispose(); } ```