Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/langium/src/default-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ export function createDefaultSharedCoreModule(context: DefaultSharedCoreModuleCo
WorkspaceManager: (services) => new DefaultWorkspaceManager(services),
FileSystemProvider: (services) => context.fileSystemProvider(services),
WorkspaceLock: () => new DefaultWorkspaceLock(),
ConfigurationProvider: (services) => new DefaultConfigurationProvider(services)
}
ConfigurationProvider: (services) => new DefaultConfigurationProvider(services),
},
profilers: {}
};
}
61 changes: 53 additions & 8 deletions packages/langium/src/parser/langium-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
******************************************************************************/

/* eslint-disable @typescript-eslint/no-explicit-any */
import type { DSLMethodOpts, ILexingError, IOrAlt, IParserErrorMessageProvider, IRecognitionException, IToken, TokenType, TokenVocabulary, IRuleConfig } from 'chevrotain';
import type { AbstractElement, Action, Assignment, InfixRule, ParserRule } from '../languages/generated/ast.js';
import { isInfixRule } from '../languages/generated/ast.js';
import type { AbstractElement, Action, Assignment, InfixRule, ParserRule } from '../languages/generated/ast.js';
import type { DSLMethodOpts, ILexingError, IOrAlt, IParserErrorMessageProvider, IRecognitionException, IToken, ParserMethod, SubruleMethodOpts, TokenType, TokenVocabulary, IRuleConfig } from 'chevrotain';
import type { Linker } from '../references/linker.js';
import type { LangiumCoreServices } from '../services.js';
import type { AstNode, AstReflection, CompositeCstNode, CstNode } from '../syntax-tree.js';
Expand All @@ -21,6 +21,7 @@ import { getExplicitRuleType, isDataTypeRule } from '../utils/grammar-utils.js';
import { assignMandatoryProperties, getContainerOfType, linkContentToContainer } from '../utils/ast-utils.js';
import { CstNodeBuilder } from './cst-node-builder.js';
import type { LexingReport } from './token-builder.js';
import type { ProfilingTask } from '../workspace/profiler.js';

export type ParseResult<T = AstNode> = {
value: T,
Expand Down Expand Up @@ -140,11 +141,19 @@ export abstract class AbstractLangiumParser implements BaseParser {
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
});
if (services.shared.profilers.LangiumProfiler?.isActive('parsing')) {
this.wrapper = new ProfilerWrapper(tokens, {
...services.parser.ParserConfig,
skipValidations: production,
errorMessageProvider: services.parser.ParserErrorMessageProvider
}, services.shared.profilers.LangiumProfiler.createTask('parsing', services.LanguageMetaData.languageId));
} else {
this.wrapper = new ChevrotainWrapper(tokens, {
...services.parser.ParserConfig,
skipValidations: production,
errorMessageProvider: services.parser.ParserErrorMessageProvider
});
}
}

alternatives(idx: number, choices: Array<IOrAlt<any>>): void {
Expand Down Expand Up @@ -283,7 +292,7 @@ export class LangiumParser extends AbstractLangiumParser {
}

private doParse(rule: RuleResult): any {
let result = rule.call(this.wrapper, {});
let result = this.wrapper.rule(rule);
if (this.stack.length > 0) {
// In case the parser throws on the entry rule, `construct` is not called
// We need to call it manually here
Expand Down Expand Up @@ -846,4 +855,40 @@ class ChevrotainWrapper extends EmbeddedActionsParser {
wrapAtLeastOne(idx: number, callback: DSLMethodOpts<unknown>): void {
this.atLeastOne(idx, callback);
}
rule(rule: RuleResult): any {
return rule.call(this, {});
}
}

class ProfilerWrapper extends ChevrotainWrapper {
private readonly task: ProfilingTask;
constructor(tokens: TokenVocabulary, config: IParserConfig, task: ProfilingTask) {
super(tokens, config);
this.task = task;
}

override rule(rule: RuleResult): any {
this.task.start();
this.task.startSubTask(this.ruleName(rule));
try {
return super.rule(rule);
}
finally {
this.task.stopSubTask(this.ruleName(rule));
this.task.stop();
}
}

private ruleName(rule: any): string {
return rule.ruleName as string;
}
protected override subrule<ARGS extends unknown[], R>(idx: number, ruleToCall: ParserMethod<ARGS, R>, options?: SubruleMethodOpts<ARGS>): R {
this.task.startSubTask(this.ruleName(ruleToCall));
try {
return super.subrule<ARGS, R>(idx, ruleToCall, options);
}
finally {
this.task.stopSubTask(this.ruleName(ruleToCall));
}
}
}
33 changes: 30 additions & 3 deletions packages/langium/src/references/linker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { isAstNode, isAstNodeDescription, isLinkingError } from '../syntax-tree.
import { findRootNode, streamAst, streamReferences } from '../utils/ast-utils.js';
import { interruptAndCheck } from '../utils/promise-utils.js';
import { DocumentState } from '../workspace/documents.js';
import type { LangiumProfiler } from '../workspace/profiler.js';

/**
* Language-specific service for resolving cross-references in the AST.
Expand Down Expand Up @@ -95,18 +96,44 @@ export class DefaultLinker implements Linker {
protected readonly scopeProvider: ScopeProvider;
protected readonly astNodeLocator: AstNodeLocator;
protected readonly langiumDocuments: () => LangiumDocuments;
protected readonly profiler: LangiumProfiler | undefined;
protected readonly languageId: string;

constructor(services: LangiumCoreServices) {
this.reflection = services.shared.AstReflection;
this.langiumDocuments = () => services.shared.workspace.LangiumDocuments;
this.scopeProvider = services.references.ScopeProvider;
this.astNodeLocator = services.workspace.AstNodeLocator;
this.profiler = services.shared.profilers.LangiumProfiler;
this.languageId = services.LanguageMetaData.languageId;
}

async link(document: LangiumDocument, cancelToken = CancellationToken.None): Promise<void> {
for (const node of streamAst(document.parseResult.value)) {
await interruptAndCheck(cancelToken);
streamReferences(node).forEach(ref => this.doLink(ref, document));
if (this.profiler?.isActive('linking')) {
const task = this.profiler.createTask('linking', this.languageId);
task.start();
try {
for (const node of streamAst(document.parseResult.value)) {
await interruptAndCheck(cancelToken);
streamReferences(node).forEach(ref => {
const name = `${node.$type}:${ref.property}`;
task.startSubTask(name);
try {
this.doLink(ref, document);
} finally {
task.stopSubTask(name);
}
});
}
} finally {
task.stop();
}
}
else {
for (const node of streamAst(document.parseResult.value)) {
await interruptAndCheck(cancelToken);
streamReferences(node).forEach(ref => this.doLink(ref, document));
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions packages/langium/src/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import type { IndexManager } from './workspace/index-manager.js';
import type { WorkspaceLock } from './workspace/workspace-lock.js';
import type { Hydrator } from './serializer/hydrator.js';
import type { WorkspaceManager } from './workspace/workspace-manager.js';
import type { LangiumProfiler } from './workspace/profiler.js';

/**
* The services generated by `langium-cli` for a specific language. These are derived from the
Expand Down Expand Up @@ -122,6 +123,9 @@ export type LangiumDefaultSharedCoreServices = {
readonly WorkspaceLock: WorkspaceLock
readonly WorkspaceManager: WorkspaceManager
}
readonly profilers: {
readonly LangiumProfiler?: LangiumProfiler
}
}

/**
Expand Down
38 changes: 32 additions & 6 deletions packages/langium/src/validation/document-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { tokenToRange } from '../utils/cst-utils.js';
import { interruptAndCheck, isOperationCancelled } from '../utils/promise-utils.js';
import { diagnosticData } from './validation-registry.js';
import type { LexingDiagnostic, LexingDiagnosticSeverity } from '../parser/token-builder.js';
import type { LangiumProfiler } from '../workspace/profiler.js';

export interface ValidationOptions {
/**
Expand Down Expand Up @@ -53,10 +54,14 @@ export class DefaultDocumentValidator implements DocumentValidator {

protected readonly validationRegistry: ValidationRegistry;
protected readonly metadata: LanguageMetaData;
protected readonly profiler: LangiumProfiler | undefined;
protected readonly languageId: string;

constructor(services: LangiumCoreServices) {
this.validationRegistry = services.validation.ValidationRegistry;
this.metadata = services.LanguageMetaData;
this.profiler = services.shared.profilers.LangiumProfiler;
this.languageId = services.LanguageMetaData.languageId;
}

async validateDocument(document: LangiumDocument, options: ValidationOptions = {}, cancelToken = CancellationToken.None): Promise<Diagnostic[]> {
Expand Down Expand Up @@ -201,13 +206,34 @@ export class DefaultDocumentValidator implements DocumentValidator {
}

protected async validateAstNodes(rootNode: AstNode, options: ValidationOptions, acceptor: ValidationAcceptor, cancelToken = CancellationToken.None): Promise<void> {
await Promise.all(streamAst(rootNode).map(async node => {
await interruptAndCheck(cancelToken);
const checks = this.validationRegistry.getChecks(node.$type, options.categories);
for (const check of checks) {
await check(node, acceptor, cancelToken);
if (this.profiler?.isActive('validating')) {
const task = this.profiler.createTask('validating', this.languageId);
task.start();
try {
streamAst(rootNode).forEach(node => {
task.startSubTask(node.$type);
try {
const checks = this.validationRegistry.getChecks(node.$type, options.categories);
for (const check of checks) {
check(node, acceptor, cancelToken);
}
} finally {
task.stopSubTask(node.$type);
}
});
} finally {
task.stop();
}
}));
}
else {
await Promise.all(streamAst(rootNode).map(async node => {
await interruptAndCheck(cancelToken);
const checks = this.validationRegistry.getChecks(node.$type, options.categories);
for (const check of checks) {
await check(node, acceptor, cancelToken);
}
}));
}
}

protected async validateAstAfter(rootNode: AstNode, options: ValidationOptions, acceptor: ValidationAcceptor, cancelToken = CancellationToken.None): Promise<void> {
Expand Down
1 change: 1 addition & 0 deletions packages/langium/src/workspace/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export * from './file-system-provider.js';
export * from './index-manager.js';
export * from './workspace-lock.js';
export * from './workspace-manager.js';
export * from './profiler.js';
Loading
Loading