From 781d365e4c6f4e82c77357d5458d747b89103983 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 19 Sep 2024 11:29:55 +0200 Subject: [PATCH] Perform parser optimizations in production mode --- packages/langium-cli/src/generator/module-generator.ts | 6 ++++-- packages/langium/src/languages/language-meta-data.ts | 9 +++++++++ packages/langium/src/parser/langium-parser.ts | 9 +++++++-- packages/langium/src/parser/lexer.ts | 4 +++- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/packages/langium-cli/src/generator/module-generator.ts b/packages/langium-cli/src/generator/module-generator.ts index 2ac6e1981..89789f6d0 100644 --- a/packages/langium-cli/src/generator/module-generator.ts +++ b/packages/langium-cli/src/generator/module-generator.ts @@ -5,13 +5,14 @@ ******************************************************************************/ import type { Grammar, IParserConfig } from 'langium'; -import { type Generated, expandToNode, joinToNode, toString } from 'langium/generate'; +import { EOL, type Generated, expandToNode, joinToNode, toString } from 'langium/generate'; import type { LangiumConfig, LangiumLanguageConfig } from '../package-types.js'; import { generatedHeader } from './node-util.js'; export function generateModule(grammars: Grammar[], config: LangiumConfig, grammarConfigMap: Map): string { const grammarsWithName = grammars.filter(grammar => !!grammar.name); const parserConfig = config.chevrotainParserConfig; + const mode = config.mode; const hasIParserConfigImport = Boolean(parserConfig) || grammars.some(grammar => grammarConfigMap.get(grammar)?.chevrotainParserConfig !== undefined); let needsGeneralParserConfig = undefined; @@ -41,12 +42,13 @@ export function generateModule(grammars: Grammar[], config: LangiumConfig, gramm grammarsWithName, grammar => { const config = grammarConfigMap.get(grammar)!; + const modeValue = mode ? `,${EOL} mode: '${mode}'` : ''; return expandToNode` export const ${ grammar.name }LanguageMetaData = { languageId: '${config.id}', fileExtensions: [${config.fileExtensions && joinToNode(config.fileExtensions, e => appendQuotesAndDot(e), { separator: ', ' })}], - caseInsensitive: ${Boolean(config.caseInsensitive)} + caseInsensitive: ${Boolean(config.caseInsensitive)}${modeValue} } as const satisfies LanguageMetaData; `; }, diff --git a/packages/langium/src/languages/language-meta-data.ts b/packages/langium/src/languages/language-meta-data.ts index aa6f5d987..9f6381e3f 100644 --- a/packages/langium/src/languages/language-meta-data.ts +++ b/packages/langium/src/languages/language-meta-data.ts @@ -4,8 +4,17 @@ * terms of the MIT License, which is available in the project root. ******************************************************************************/ +/** + * Metadata of a language. + */ export interface LanguageMetaData { languageId: string; fileExtensions: readonly string[]; caseInsensitive: boolean; + /** + * Mode used to optimize code for development or production environments. + * + * In production mode, all lexer/parser validations are disabled. + */ + mode?: 'development' | 'production'; } diff --git a/packages/langium/src/parser/langium-parser.ts b/packages/langium/src/parser/langium-parser.ts index 4d431e3aa..2eb66ce97 100644 --- a/packages/langium/src/parser/langium-parser.ts +++ b/packages/langium/src/parser/langium-parser.ts @@ -135,8 +135,10 @@ export abstract class AbstractLangiumParser implements BaseParser { constructor(services: LangiumCoreServices) { this.lexer = services.parser.Lexer; const tokens = this.lexer.definition; + const production = services.LanguageMetaData.mode === 'production'; this.wrapper = new ChevrotainWrapper(tokens, { ...services.parser.ParserConfig, + skipValidations: production, errorMessageProvider: services.parser.ParserErrorMessageProvider }); } @@ -631,13 +633,16 @@ class ChevrotainWrapper extends EmbeddedActionsParser { // This array is set in the base implementation of Chevrotain. definitionErrors: IParserDefinitionError[]; - constructor(tokens: TokenVocabulary, config?: IParserConfig) { + constructor(tokens: TokenVocabulary, config: IParserConfig) { const useDefaultLookahead = config && 'maxLookahead' in config; super(tokens, { ...defaultConfig, lookaheadStrategy: useDefaultLookahead ? new LLkLookaheadStrategy({ maxLookahead: config.maxLookahead }) - : new LLStarLookaheadStrategy(), + : new LLStarLookaheadStrategy({ + // If validations are skipped, don't log the lookahead warnings + logging: config.skipValidations ? () => { } : undefined + }), ...config, }); } diff --git a/packages/langium/src/parser/lexer.ts b/packages/langium/src/parser/lexer.ts index fedfad6fc..b387ebcad 100644 --- a/packages/langium/src/parser/lexer.ts +++ b/packages/langium/src/parser/lexer.ts @@ -51,8 +51,10 @@ export class DefaultLexer implements Lexer { }); this.tokenTypes = this.toTokenTypeDictionary(tokens); const lexerTokens = isTokenTypeDictionary(tokens) ? Object.values(tokens) : tokens; + const production = services.LanguageMetaData.mode === 'production'; this.chevrotainLexer = new ChevrotainLexer(lexerTokens, { - positionTracking: 'full' + positionTracking: 'full', + skipValidations: production }); }