refactor: Simplify langium by abstracting common functions

This commit is contained in:
Sidharth Vinod 2023-09-05 17:21:49 +05:30
parent 795baedbb1
commit 11b60ce3b8
No known key found for this signature in database
GPG Key ID: FB5CCD378D3907CD
7 changed files with 78 additions and 100 deletions

View File

@ -1,2 +1,2 @@
export * from './commonLexer.js';
export * from './commonValueConverters.js';
export * from './valueConverter.js';

View File

@ -0,0 +1,26 @@
import type { GrammarAST, Stream, TokenBuilderOptions } from 'langium';
import type { TokenType } from '../chevrotainWrapper.js';
import { DefaultTokenBuilder } from 'langium';
export class MermaidTokenBuilder extends DefaultTokenBuilder {
private keywords: Set<string>;
constructor(public _keywords: string[]) {
super();
this.keywords = new Set<string>(_keywords);
}
protected override buildKeywordTokens(
rules: Stream<GrammarAST.AbstractRule>,
terminalTokens: TokenType[],
options?: TokenBuilderOptions
): TokenType[] {
const tokenTypes: TokenType[] = super.buildKeywordTokens(rules, terminalTokens, options);
tokenTypes.forEach((tokenType: TokenType): void => {
if (this.keywords.has(tokenType.name) && tokenType.PATTERN !== undefined) {
tokenType.PATTERN = new RegExp(tokenType.PATTERN.toString() + '(?!\\S)');
}
});
return tokenTypes;
}
}

View File

@ -1,38 +1,45 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { CstNode, GrammarAST, ValueType } from 'langium';
import { DefaultValueConverter } from 'langium';
import { accessibilityDescrRegex, accessibilityTitleRegex, titleRegex } from './commonMatcher.js';
export class CommonValueConverter extends DefaultValueConverter {
export abstract class MermaidValueConverter extends DefaultValueConverter {
/**
* A method contains convert logic to be used by class.
*
* @param rule - Parsed rule.
* @param input - Matched string.
* @param cstNode - Node in the Concrete Syntax Tree (CST).
* @returns converted the value if it's available or `undefined` if it's not.
*/
protected abstract runCustomConverter(
rule: GrammarAST.AbstractRule,
input: string,
cstNode: CstNode
): ValueType | undefined;
protected override runConverter(
rule: GrammarAST.AbstractRule,
input: string,
cstNode: CstNode
): ValueType {
const value: ValueType | undefined = CommonValueConverter.customRunConverter(
rule,
input,
cstNode
);
let value: ValueType | undefined = this.runCommonConverter(rule, input, cstNode);
if (value === undefined) {
value = this.runCustomConverter(rule, input, cstNode);
}
if (value === undefined) {
return super.runConverter(rule, input, cstNode);
} else {
return value;
}
return value;
}
/**
* A method contains convert logic to be used by class itself or `MermaidValueConverter`.
*
* @param rule - Parsed rule.
* @param input - Matched string.
* @param _cstNode - Node in the Concrete Syntax Tree (CST).
* @returns converted the value if it's common rule or `undefined` if it's not.
*/
public static customRunConverter(
private runCommonConverter(
rule: GrammarAST.AbstractRule,
input: string,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_cstNode: CstNode
): ValueType | undefined {
let regex: RegExp | undefined;
@ -72,3 +79,13 @@ export class CommonValueConverter extends DefaultValueConverter {
return undefined;
}
}
export class CommonValueConverter extends MermaidValueConverter {
protected runCustomConverter(
_rule: GrammarAST.AbstractRule,
_input: string,
_cstNode: CstNode
): ValueType | undefined {
return undefined;
}
}

View File

@ -9,7 +9,7 @@ import { EmptyFileSystem, createDefaultModule, createDefaultSharedModule, inject
import { MermaidGeneratedSharedModule, InfoGeneratedModule } from '../generated/module.js';
import { CommonLexer } from '../common/commonLexer.js';
import { CommonValueConverter } from '../common/commonValueConverters.js';
import { CommonValueConverter } from '../common/valueConverter.js';
import { InfoTokenBuilder } from './infoTokenBuilder.js';
/**

View File

@ -1,24 +1,7 @@
import type { GrammarAST, Stream, TokenBuilderOptions } from 'langium';
import { DefaultTokenBuilder } from 'langium';
import { MermaidTokenBuilder } from '../common/tokenBuilder.js';
import type { TokenType } from '../chevrotainWrapper.js';
export class InfoTokenBuilder extends DefaultTokenBuilder {
protected override buildKeywordTokens(
rules: Stream<GrammarAST.AbstractRule>,
terminalTokens: TokenType[],
options?: TokenBuilderOptions
): TokenType[] {
const tokenTypes: TokenType[] = super.buildKeywordTokens(rules, terminalTokens, options);
// to restrict users, they mustn't have any non-whitespace characters after the keyword.
tokenTypes.forEach((tokenType: TokenType): void => {
if (
(tokenType.name === 'info' || tokenType.name === 'showInfo') &&
tokenType.PATTERN !== undefined
) {
tokenType.PATTERN = new RegExp(tokenType.PATTERN.toString() + '(?!\\S)');
}
});
return tokenTypes;
export class InfoTokenBuilder extends MermaidTokenBuilder {
constructor() {
super(['info', 'showInfo']);
}
}

View File

@ -1,23 +1,7 @@
import type { GrammarAST, Stream, TokenBuilderOptions } from 'langium';
import { DefaultTokenBuilder } from 'langium';
import { MermaidTokenBuilder } from '../common/tokenBuilder.js';
import type { TokenType } from '../chevrotainWrapper.js';
export class PieTokenBuilder extends DefaultTokenBuilder {
protected override buildKeywordTokens(
rules: Stream<GrammarAST.AbstractRule>,
terminalTokens: TokenType[],
options?: TokenBuilderOptions
): TokenType[] {
const tokenTypes: TokenType[] = super.buildKeywordTokens(rules, terminalTokens, options);
tokenTypes.forEach((tokenType: TokenType): void => {
if (
(tokenType.name === 'pie' || tokenType.name === 'showData') &&
tokenType.PATTERN !== undefined
) {
tokenType.PATTERN = new RegExp(tokenType.PATTERN.toString() + '(?!\\S)');
}
});
return tokenTypes;
export class PieTokenBuilder extends MermaidTokenBuilder {
constructor() {
super(['pie', 'showData']);
}
}

View File

@ -1,49 +1,17 @@
import type { CstNode, GrammarAST, ValueType } from 'langium';
import { DefaultValueConverter } from 'langium';
import { MermaidValueConverter } from '../common/valueConverter.js';
import { CommonValueConverter } from '../common/commonValueConverters.js';
export class PieValueConverter extends DefaultValueConverter {
protected override runConverter(
rule: GrammarAST.AbstractRule,
input: string,
cstNode: CstNode
): ValueType {
let value: ValueType | undefined = CommonValueConverter.customRunConverter(
rule,
input,
cstNode
);
if (value === undefined) {
value = PieValueConverter.customRunConverter(rule, input, cstNode);
}
if (value === undefined) {
return super.runConverter(rule, input, cstNode);
}
return value;
}
/**
* A method contains convert logic to be used by class itself or `MermaidValueConverter`.
*
* @param rule - Parsed rule.
* @param input - Matched string.
* @param _cstNode - Node in the Concrete Syntax Tree (CST).
* @returns converted the value if it's pie rule or `null` if it's not.
*/
public static customRunConverter(
export class PieValueConverter extends MermaidValueConverter {
override runCustomConverter(
rule: GrammarAST.AbstractRule,
input: string,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_cstNode: CstNode
): ValueType | undefined {
if (rule.name === 'PIE_SECTION_LABEL') {
return input
.replace(/"/g, '')
.trim()
.replaceAll(/[\t ]{2,}/gm, ' ');
if (rule.name !== 'PIE_SECTION_LABEL') {
return undefined;
}
return undefined;
return input.replace(/"/g, '').trim();
}
}