diff --git a/examples/arithmetics/src/language-server/generated/module.ts b/examples/arithmetics/src/language-server/generated/module.ts index ec9a3cc14..3728403d8 100644 --- a/examples/arithmetics/src/language-server/generated/module.ts +++ b/examples/arithmetics/src/language-server/generated/module.ts @@ -3,11 +3,11 @@ * DO NOT EDIT MANUALLY! ******************************************************************************/ -import { LangiumGeneratedServices, LangiumServices, Module } from 'langium'; +import { LangiumGeneratedServices, LangiumServices, LanguageMetaData, Module } from 'langium'; import { ArithmeticsAstReflection } from './ast'; import { grammar } from './grammar'; -export const languageMetaData = { +export const languageMetaData: LanguageMetaData = { languageId: 'arithmetics', fileExtensions: ['.calc'] }; @@ -15,5 +15,6 @@ export const languageMetaData = { export const ArithmeticsGeneratedModule: Module = { Grammar: () => grammar(), AstReflection: () => new ArithmeticsAstReflection(), - LanguageMetaData: () => languageMetaData + LanguageMetaData: () => languageMetaData, + parser: {} }; diff --git a/examples/arithmetics/syntaxes/arithmetics.tmLanguage.json b/examples/arithmetics/syntaxes/arithmetics.tmLanguage.json index 06b2df3ac..ef516753c 100644 --- a/examples/arithmetics/syntaxes/arithmetics.tmLanguage.json +++ b/examples/arithmetics/syntaxes/arithmetics.tmLanguage.json @@ -5,9 +5,6 @@ ".calc" ], "patterns": [ - { - "include": "#comments" - }, { "name": "keyword.control.arithmetics", "match": "\\b(def|module)\\b" diff --git a/examples/domainmodel/langium-config.json b/examples/domainmodel/langium-config.json index 71e57a6f9..d442821dd 100644 --- a/examples/domainmodel/langium-config.json +++ b/examples/domainmodel/langium-config.json @@ -5,5 +5,10 @@ "out": "src/language-server/generated", "textMate": { "out": "syntaxes/domain-model.tmLanguage.json" + }, + "chevrotainParserConfig": { + "recoveryEnabled": true, + "nodeLocationTracking": "onlyOffset", + "maxLookahead": 3 } } diff --git a/examples/domainmodel/src/language-server/generated/module.ts b/examples/domainmodel/src/language-server/generated/module.ts index eed83f55a..7864991ca 100644 --- a/examples/domainmodel/src/language-server/generated/module.ts +++ b/examples/domainmodel/src/language-server/generated/module.ts @@ -3,17 +3,26 @@ * DO NOT EDIT MANUALLY! ******************************************************************************/ -import { LangiumGeneratedServices, LangiumServices, Module } from 'langium'; +import { LangiumGeneratedServices, LangiumServices, LanguageMetaData, Module, IParserConfig } from 'langium'; import { DomainModelAstReflection } from './ast'; import { grammar } from './grammar'; -export const languageMetaData = { +export const languageMetaData: LanguageMetaData = { languageId: 'domain-model', fileExtensions: ['.dmodel'] }; +export const parserConfig: IParserConfig = { + recoveryEnabled: true, + nodeLocationTracking: 'onlyOffset', + maxLookahead: 3, +}; + export const DomainModelGeneratedModule: Module = { Grammar: () => grammar(), AstReflection: () => new DomainModelAstReflection(), - LanguageMetaData: () => languageMetaData + LanguageMetaData: () => languageMetaData, + parser: { + ParserConfig: () => parserConfig + } }; diff --git a/examples/statemachine/src/language-server/generated/module.ts b/examples/statemachine/src/language-server/generated/module.ts index c73fa52a1..ebecf857f 100644 --- a/examples/statemachine/src/language-server/generated/module.ts +++ b/examples/statemachine/src/language-server/generated/module.ts @@ -3,11 +3,11 @@ * DO NOT EDIT MANUALLY! ******************************************************************************/ -import { LangiumGeneratedServices, LangiumServices, Module } from 'langium'; +import { LangiumGeneratedServices, LangiumServices, LanguageMetaData, Module } from 'langium'; import { StatemachineAstReflection } from './ast'; import { grammar } from './grammar'; -export const languageMetaData = { +export const languageMetaData: LanguageMetaData = { languageId: 'statemachine', fileExtensions: ['.statemachine'] }; @@ -15,5 +15,6 @@ export const languageMetaData = { export const StatemachineGeneratedModule: Module = { Grammar: () => grammar(), AstReflection: () => new StatemachineAstReflection(), - LanguageMetaData: () => languageMetaData + LanguageMetaData: () => languageMetaData, + parser: {} }; diff --git a/packages/langium-cli/langium-config-schema.json b/packages/langium-cli/langium-config-schema.json index 041c707fd..31d45734a 100644 --- a/packages/langium-cli/langium-config-schema.json +++ b/packages/langium-cli/langium-config-schema.json @@ -40,6 +40,37 @@ "out" ] }, + "chevrotainParserConfig": { + "description": "An object to describe the Chevrotain parser configuration", + "type": "object", + "properties": { + "recoveryEnabled": { + "description": "Is the error recovery / fault tolerance of the Chevrotain Parser enabled", + "type": "boolean" + }, + "maxLookahead": { + "description": "Maximum number of tokens the parser will use to choose between alternatives", + "type": "number" + }, + "dynamicTokensEnabled": { + "description": "A flag to support Dynamically defined Tokens", + "type": "boolean" + }, + "nodeLocationTracking": { + "description": "Enable computation of CST nodes location", + "type": "string" + }, + "traceInitPerf": { + "description": "A flag to print performance tracing logs during parser initialization", + "type": ["boolean", "number"] + }, + "skipValidations": { + "description": "A flag to avoid running the grammar validations during Parser initialization", + "type": "boolean" + } + }, + "required": [] + }, "langiumInternal": { "description": "A flag to determine whether langium uses itself to bootstrap", "type": "boolean" diff --git a/packages/langium-cli/src/generator/module-generator.ts b/packages/langium-cli/src/generator/module-generator.ts index b1db5d3f3..692267972 100644 --- a/packages/langium-cli/src/generator/module-generator.ts +++ b/packages/langium-cli/src/generator/module-generator.ts @@ -10,33 +10,56 @@ import { LangiumConfig } from '../package'; import { generatedHeader } from './util'; export function generateModule(grammar: langium.Grammar, config: LangiumConfig): string { + const parserConfig = config.chevrotainParserConfig; const node = new CompositeGeneratorNode(); + node.append(generatedHeader); if (config.langiumInternal) { + node.append(`import { LanguageMetaData${parserConfig ? ', IParserConfig' : ''} } from '../..';`, NL); node.append("import { Module } from '../../dependency-injection';", NL); node.contents.push("import { LangiumGeneratedServices, LangiumServices } from '../../services';", NL); } else { - node.append("import { LangiumGeneratedServices, LangiumServices, Module } from 'langium';", NL); + node.append(`import { LangiumGeneratedServices, LangiumServices, LanguageMetaData, Module${parserConfig ? ', IParserConfig' : ''} } from 'langium';`, NL); } node.append( 'import { ', grammar.name, "AstReflection } from './ast';", NL, "import { grammar } from './grammar';", NL, NL ); - node.append('export const languageMetaData = {', NL); + node.append('export const languageMetaData: LanguageMetaData = {', NL); node.indent(metaData => { metaData.append(`languageId: '${config.languageId}',`, NL); metaData.append(`fileExtensions: [${config.fileExtensions && config.fileExtensions.map(e => appendQuotesAndDot(e)).join(', ')}]`, NL); }); node.append('};', NL, NL); + if (parserConfig) { + node.append('export const parserConfig: IParserConfig = {', NL); + node.indent(configNode => { + Object.keys(parserConfig).forEach(key => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const value = (parserConfig as any)[key]; + configNode.append(`${key}: ${typeof value === 'string' ? `'${value}'` : value},`, NL); + }); + }); + node.append('};', NL, NL); + } + node.append('export const ', grammar.name, 'GeneratedModule: Module = {', NL); node.indent(moduleNode => { moduleNode.append( 'Grammar: () => grammar(),', NL, 'AstReflection: () => new ', grammar.name, 'AstReflection(),', NL, - 'LanguageMetaData: () => languageMetaData', NL + 'LanguageMetaData: () => languageMetaData,', NL, + 'parser: {', ); + if (parserConfig) { + moduleNode.append(NL); + moduleNode.indent(parserGroupNode => { + parserGroupNode.append('ParserConfig: () => parserConfig', NL); + }); + } + moduleNode.append('}', NL); }); node.append('};', NL); diff --git a/packages/langium-cli/src/package.ts b/packages/langium-cli/src/package.ts index 80cae84c7..d6d8af4fd 100644 --- a/packages/langium-cli/src/package.ts +++ b/packages/langium-cli/src/package.ts @@ -5,6 +5,7 @@ ******************************************************************************/ import fs from 'fs-extra'; +import { IParserConfig } from 'langium'; import path from 'path'; import { getTime } from './generator/util'; @@ -32,6 +33,8 @@ export interface LangiumConfig { /** Output path to syntax highlighting file */ out: string } + /** Configure the chevrotain parser */ + chevrotainParserConfig?: IParserConfig, /** The following option is meant to be used only by Langium itself */ langiumInternal?: boolean } diff --git a/packages/langium/src/grammar/generated/module.ts b/packages/langium/src/grammar/generated/module.ts index 91e73683c..544f1cba6 100644 --- a/packages/langium/src/grammar/generated/module.ts +++ b/packages/langium/src/grammar/generated/module.ts @@ -3,12 +3,13 @@ * DO NOT EDIT MANUALLY! ******************************************************************************/ +import { LanguageMetaData } from '../..'; import { Module } from '../../dependency-injection'; import { LangiumGeneratedServices, LangiumServices } from '../../services'; import { LangiumGrammarAstReflection } from './ast'; import { grammar } from './grammar'; -export const languageMetaData = { +export const languageMetaData: LanguageMetaData = { languageId: 'langium', fileExtensions: ['.langium'] }; @@ -16,5 +17,6 @@ export const languageMetaData = { export const LangiumGrammarGeneratedModule: Module = { Grammar: () => grammar(), AstReflection: () => new LangiumGrammarAstReflection(), - LanguageMetaData: () => languageMetaData + LanguageMetaData: () => languageMetaData, + parser: {} }; diff --git a/packages/langium/src/index.ts b/packages/langium/src/index.ts index a651d24ab..4a464b2f5 100644 --- a/packages/langium/src/index.ts +++ b/packages/langium/src/index.ts @@ -24,6 +24,7 @@ export * from './lsp/language-server'; export * from './validation/document-validator'; export * from './validation/validation-registry'; export * from './parser/langium-parser'; +export * from './parser/parser-config'; export * from './references/linker'; export * from './references/naming'; export * from './references/scope'; diff --git a/packages/langium/src/parser/langium-parser.ts b/packages/langium/src/parser/langium-parser.ts index 2460ebe4b..6cdea1bde 100644 --- a/packages/langium/src/parser/langium-parser.ts +++ b/packages/langium/src/parser/langium-parser.ts @@ -14,6 +14,7 @@ import { Linker } from '../references/linker'; import { LangiumServices } from '../services'; import { getContainerOfType } from '../utils/ast-util'; import { ValueConverter } from './value-converter'; +import { IParserConfig } from './parser-config'; export type ParseResult = { value: T, @@ -39,7 +40,7 @@ export class LangiumParser { } constructor(services: LangiumServices, tokens: TokenType[]) { - this.wrapper = new ChevrotainWrapper(tokens); + this.wrapper = new ChevrotainWrapper(tokens, services.parser.ParserConfig); this.linker = services.references.Linker; this.converter = services.parser.ValueConverter; this.lexer = new Lexer(tokens); @@ -302,8 +303,12 @@ class ChevrotainWrapper extends EmbeddedActionsParser { private analysed = false; - constructor(tokens: TokenType[]) { - super(tokens, { recoveryEnabled: true, nodeLocationTracking: 'onlyOffset' }); + constructor(tokens: TokenType[], config?: IParserConfig) { + super(tokens, { + recoveryEnabled: true, + nodeLocationTracking: 'onlyOffset', + ...config + }); } get IS_RECORDING(): boolean { diff --git a/packages/langium/src/parser/parser-config.ts b/packages/langium/src/parser/parser-config.ts new file mode 100644 index 000000000..f90e28968 --- /dev/null +++ b/packages/langium/src/parser/parser-config.ts @@ -0,0 +1,7 @@ +/****************************************************************************** + * Copyright 2021 TypeFox GmbH + * This program and the accompanying materials are made available under the + * terms of the MIT License, which is available in the project root. + ******************************************************************************/ + +export { IParserConfig } from 'chevrotain'; diff --git a/packages/langium/src/services.ts b/packages/langium/src/services.ts index 623f3c2fd..5d0d59530 100644 --- a/packages/langium/src/services.ts +++ b/packages/langium/src/services.ts @@ -6,7 +6,7 @@ import { Connection, TextDocuments } from 'vscode-languageserver'; import { TextDocument } from 'vscode-languageserver-textdocument'; -import { AstReflection, CompletionProvider, DocumentBuilder, LangiumDocumentFactory, LangiumDocuments, DocumentValidator, Grammar, JsonSerializer, LangiumParser, LanguageMetaData, Linker, NameProvider, RuleInterpreter, ScopeComputation, ScopeProvider, TextDocumentFactory, ValidationRegistry } from '.'; +import { AstReflection, CompletionProvider, DocumentBuilder, LangiumDocumentFactory, LangiumDocuments, DocumentValidator, Grammar, JsonSerializer, LangiumParser, LanguageMetaData, Linker, NameProvider, RuleInterpreter, ScopeComputation, ScopeProvider, TextDocumentFactory, ValidationRegistry, IParserConfig } from '.'; import { AstNodeDescriptionProvider, ReferenceDescriptionProvider } from './index/ast-descriptions'; import { AstNodeLocator } from './index/ast-node-locator'; import { IndexManager } from './index/index-manager'; @@ -26,6 +26,9 @@ export type LangiumGeneratedServices = { Grammar: Grammar AstReflection: AstReflection LanguageMetaData: LanguageMetaData + parser: { + ParserConfig?: IParserConfig + } } export type LangiumLspServices = {