From 11b60ce3b861374ed571a52f2d34ad8cf6e611cc Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Tue, 5 Sep 2023 17:21:49 +0530 Subject: [PATCH] refactor: Simplify langium by abstracting common functions --- packages/parser/src/language/common/index.ts | 2 +- .../src/language/common/tokenBuilder.ts | 26 +++++++++ ...onValueConverters.ts => valueConverter.ts} | 53 ++++++++++++------- .../parser/src/language/info/infoModule.ts | 2 +- .../src/language/info/infoTokenBuilder.ts | 25 ++------- .../src/language/pie/pieTokenBuilder.ts | 24 ++------- .../src/language/pie/pieValueConverter.ts | 46 +++------------- 7 files changed, 78 insertions(+), 100 deletions(-) create mode 100644 packages/parser/src/language/common/tokenBuilder.ts rename packages/parser/src/language/common/{commonValueConverters.ts => valueConverter.ts} (62%) diff --git a/packages/parser/src/language/common/index.ts b/packages/parser/src/language/common/index.ts index 554e7902c..c69c93fcc 100644 --- a/packages/parser/src/language/common/index.ts +++ b/packages/parser/src/language/common/index.ts @@ -1,2 +1,2 @@ export * from './commonLexer.js'; -export * from './commonValueConverters.js'; +export * from './valueConverter.js'; diff --git a/packages/parser/src/language/common/tokenBuilder.ts b/packages/parser/src/language/common/tokenBuilder.ts new file mode 100644 index 000000000..e815a82bd --- /dev/null +++ b/packages/parser/src/language/common/tokenBuilder.ts @@ -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; + constructor(public _keywords: string[]) { + super(); + this.keywords = new Set(_keywords); + } + + protected override buildKeywordTokens( + rules: Stream, + 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; + } +} diff --git a/packages/parser/src/language/common/commonValueConverters.ts b/packages/parser/src/language/common/valueConverter.ts similarity index 62% rename from packages/parser/src/language/common/commonValueConverters.ts rename to packages/parser/src/language/common/valueConverter.ts index 228746c8a..75bc182ac 100644 --- a/packages/parser/src/language/common/commonValueConverters.ts +++ b/packages/parser/src/language/common/valueConverter.ts @@ -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; + } +} diff --git a/packages/parser/src/language/info/infoModule.ts b/packages/parser/src/language/info/infoModule.ts index 96dfd5a84..c6e520d11 100644 --- a/packages/parser/src/language/info/infoModule.ts +++ b/packages/parser/src/language/info/infoModule.ts @@ -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'; /** diff --git a/packages/parser/src/language/info/infoTokenBuilder.ts b/packages/parser/src/language/info/infoTokenBuilder.ts index b71fa30ad..ddb68de14 100644 --- a/packages/parser/src/language/info/infoTokenBuilder.ts +++ b/packages/parser/src/language/info/infoTokenBuilder.ts @@ -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, - 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']); } } diff --git a/packages/parser/src/language/pie/pieTokenBuilder.ts b/packages/parser/src/language/pie/pieTokenBuilder.ts index 2004ed50b..7c17eb1c7 100644 --- a/packages/parser/src/language/pie/pieTokenBuilder.ts +++ b/packages/parser/src/language/pie/pieTokenBuilder.ts @@ -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, - 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']); } } diff --git a/packages/parser/src/language/pie/pieValueConverter.ts b/packages/parser/src/language/pie/pieValueConverter.ts index 6e31709af..fcc21e7a9 100644 --- a/packages/parser/src/language/pie/pieValueConverter.ts +++ b/packages/parser/src/language/pie/pieValueConverter.ts @@ -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(); } }