diff --git a/examples/arithmetics/src/language-server/generated/module.ts b/examples/arithmetics/src/language-server/generated/module.ts index b94eefffa..c3f024fc8 100644 --- a/examples/arithmetics/src/language-server/generated/module.ts +++ b/examples/arithmetics/src/language-server/generated/module.ts @@ -10,7 +10,8 @@ import { ArithmeticsGrammar } from './grammar.js'; export const ArithmeticsLanguageMetaData = { languageId: 'arithmetics', fileExtensions: ['.calc'], - caseInsensitive: true + caseInsensitive: true, + mode: 'development' } as const satisfies LanguageMetaData; export const ArithmeticsGeneratedSharedModule: Module = { diff --git a/examples/domainmodel/src/language-server/generated/module.ts b/examples/domainmodel/src/language-server/generated/module.ts index c432c8821..110b44663 100644 --- a/examples/domainmodel/src/language-server/generated/module.ts +++ b/examples/domainmodel/src/language-server/generated/module.ts @@ -10,7 +10,8 @@ import { DomainModelGrammar } from './grammar.js'; export const DomainModelLanguageMetaData = { languageId: 'domain-model', fileExtensions: ['.dmodel'], - caseInsensitive: false + caseInsensitive: false, + mode: 'development' } as const satisfies LanguageMetaData; export const parserConfig: IParserConfig = { diff --git a/examples/requirements/src/language-server/generated/module.ts b/examples/requirements/src/language-server/generated/module.ts index b502f3f9d..d23da3f71 100644 --- a/examples/requirements/src/language-server/generated/module.ts +++ b/examples/requirements/src/language-server/generated/module.ts @@ -10,13 +10,15 @@ import { RequirementsGrammar, TestsGrammar } from './grammar.js'; export const RequirementsLanguageMetaData = { languageId: 'requirements-lang', fileExtensions: ['.req'], - caseInsensitive: false + caseInsensitive: false, + mode: 'development' } as const satisfies LanguageMetaData; export const TestsLanguageMetaData = { languageId: 'tests-lang', fileExtensions: ['.tst'], - caseInsensitive: false + caseInsensitive: false, + mode: 'development' } as const satisfies LanguageMetaData; export const RequirementsAndTestsGeneratedSharedModule: Module = { diff --git a/examples/statemachine/src/language-server/generated/module.ts b/examples/statemachine/src/language-server/generated/module.ts index b8b3154ea..8fb40db2a 100644 --- a/examples/statemachine/src/language-server/generated/module.ts +++ b/examples/statemachine/src/language-server/generated/module.ts @@ -10,7 +10,8 @@ import { StatemachineGrammar } from './grammar.js'; export const StatemachineLanguageMetaData = { languageId: 'statemachine', fileExtensions: ['.statemachine'], - caseInsensitive: false + caseInsensitive: false, + mode: 'development' } as const satisfies LanguageMetaData; export const StatemachineGeneratedSharedModule: Module = { diff --git a/packages/generator-langium/templates/core/.package.json b/packages/generator-langium/templates/core/.package.json index 495284ab0..ce99490bc 100644 --- a/packages/generator-langium/templates/core/.package.json +++ b/packages/generator-langium/templates/core/.package.json @@ -12,6 +12,7 @@ "watch": "tsc -b <%= tsconfig %> --watch", "lint": "eslint src --ext ts", "langium:generate": "langium generate", + "langium:generate:production": "langium generate --mode=production", "langium:watch": "langium generate --watch" }, "dependencies": { diff --git a/packages/langium-cli/src/generator/module-generator.ts b/packages/langium-cli/src/generator/module-generator.ts index 2ac6e1981..03302677b 100644 --- a/packages/langium-cli/src/generator/module-generator.ts +++ b/packages/langium-cli/src/generator/module-generator.ts @@ -12,6 +12,7 @@ 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,14 @@ export function generateModule(grammars: Grammar[], config: LangiumConfig, gramm grammarsWithName, grammar => { const config = grammarConfigMap.get(grammar)!; + const modeValue = mode === 'production' ? mode : 'development'; 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)}, + mode: '${modeValue}' } as const satisfies LanguageMetaData; `; }, diff --git a/packages/langium-cli/src/parser-validation.ts b/packages/langium-cli/src/parser-validation.ts index 11e38c4fa..e206b1475 100644 --- a/packages/langium-cli/src/parser-validation.ts +++ b/packages/langium-cli/src/parser-validation.ts @@ -59,7 +59,8 @@ function languageConfigToMetaData(config: LangiumLanguageConfig): LanguageMetaDa return { languageId: config.id, fileExtensions: config.fileExtensions ?? [], - caseInsensitive: Boolean(config.caseInsensitive) + caseInsensitive: Boolean(config.caseInsensitive), + mode: 'development' }; } diff --git a/packages/langium/src/grammar/generated/module.ts b/packages/langium/src/grammar/generated/module.ts index ad009b1e8..94d62306f 100644 --- a/packages/langium/src/grammar/generated/module.ts +++ b/packages/langium/src/grammar/generated/module.ts @@ -13,7 +13,8 @@ import { LangiumGrammarGrammar } from './grammar.js'; export const LangiumGrammarLanguageMetaData = { languageId: 'langium', fileExtensions: ['.langium'], - caseInsensitive: false + caseInsensitive: false, + mode: 'development' } as const satisfies LanguageMetaData; export const LangiumGrammarParserConfig: IParserConfig = { diff --git a/packages/langium/src/grammar/internal-grammar-util.ts b/packages/langium/src/grammar/internal-grammar-util.ts index af24d324f..a784bc9a0 100644 --- a/packages/langium/src/grammar/internal-grammar-util.ts +++ b/packages/langium/src/grammar/internal-grammar-util.ts @@ -182,7 +182,8 @@ export async function createServicesForGrammar = { AstReflection: () => interpretAstReflection(grammarNode), diff --git a/packages/langium/src/languages/language-meta-data.ts b/packages/langium/src/languages/language-meta-data.ts index aa6f5d987..9374aee21 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 Chevrotain 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 }); }