diff --git a/packages/parser/src/language/chevrotainWrapper.ts b/packages/parser/src/language/chevrotainWrapper.ts new file mode 100644 index 000000000..bde44a22e --- /dev/null +++ b/packages/parser/src/language/chevrotainWrapper.ts @@ -0,0 +1,51 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +type CustomPatternMatcherReturn = [string] & { payload?: any }; + +export type CustomPatternMatcherFunc = ( + text: string, + offset: number, + tokens: IToken[], + groups: { + [groupName: string]: IToken[]; + } +) => CustomPatternMatcherReturn | RegExpExecArray | null; + +interface ICustomPattern { + exec: CustomPatternMatcherFunc; +} + +type TokenPattern = RegExp | string | CustomPatternMatcherFunc | ICustomPattern; + +export interface IToken { + image: string; + startOffset: number; + startLine?: number; + startColumn?: number; + endOffset?: number; + endLine?: number; + endColumn?: number; + isInsertedInRecovery?: boolean; + tokenTypeIdx: number; + tokenType: TokenType; + payload?: any; +} + +export interface TokenType { + name: string; + GROUP?: string; + PATTERN?: TokenPattern; + LABEL?: string; + LONGER_ALT?: TokenType | TokenType[]; + POP_MODE?: boolean; + PUSH_MODE?: string; + LINE_BREAKS?: boolean; + CATEGORIES?: TokenType[]; + tokenTypeIdx?: number; + categoryMatches?: number[]; + categoryMatchesMap?: { + [tokType: number]: boolean; + }; + isParent?: boolean; + START_CHARS_HINT?: (string | number)[]; +} diff --git a/packages/parser/src/language/common/common.langium b/packages/parser/src/language/common/common.langium new file mode 100644 index 000000000..6a2af29e4 --- /dev/null +++ b/packages/parser/src/language/common/common.langium @@ -0,0 +1,14 @@ +fragment TitleAndAccessibilities: + ((accDescr=ACC_DESCR | accTitle=ACC_TITLE | title=TITLE) NEWLINE+)+ +; + +terminal NEWLINE: /\r?\n/; +terminal ACC_DESCR: /accDescr(?:[\t ]*:[\t ]*[^\n\r]*?(?=%%)|\s*{[^}]*})|accDescr(?:[\t ]*:[\t ]*[^\n\r]*|\s*{[^}]*})/; +terminal ACC_TITLE: /accTitle[\t ]*:[\t ]*[^\n\r]*?(?=%%)|accTitle[\t ]*:[\t ]*[^\n\r]*/; +terminal TITLE: /title(?:[\t ]+[^\n\r]*?|)(?=%%)|title(?:[\t ]+[^\n\r]*|)/; + +hidden terminal WHITESPACE: /[\t ]+/; +// TODO: add YAML_COMMENT hidden rule without interfere actual grammar +hidden terminal YAML: /---[\t ]*\r?\n[\S\s]*?---[\t ]*(?!.)/; +hidden terminal DIRECTIVE: /[\t ]*%%{[\S\s]*?}%%\s*/; +hidden terminal SINGLE_LINE_COMMENT: /[\t ]*%%[^\n\r]*/; diff --git a/packages/parser/src/language/common/commonLexer.ts b/packages/parser/src/language/common/commonLexer.ts new file mode 100644 index 000000000..e4f1272b2 --- /dev/null +++ b/packages/parser/src/language/common/commonLexer.ts @@ -0,0 +1,8 @@ +import type { LexerResult } from 'langium'; +import { DefaultLexer } from 'langium'; + +export class CommonLexer extends DefaultLexer { + public override tokenize(text: string): LexerResult { + return super.tokenize(text + '\n'); + } +} diff --git a/packages/parser/src/language/common/commonMatcher.ts b/packages/parser/src/language/common/commonMatcher.ts new file mode 100644 index 000000000..36c9cbe92 --- /dev/null +++ b/packages/parser/src/language/common/commonMatcher.ts @@ -0,0 +1,14 @@ +/** + * Matches single and multiline accessible description + */ +export const accessibilityDescrRegex = /accDescr(?:[\t ]*:[\t ]*([^\n\r]*)|\s*{([^}]*)})/; + +/** + * Matches single line accessible title + */ +export const accessibilityTitleRegex = /accTitle[\t ]*:[\t ]*([^\n\r]*)/; + +/** + * Matches a single line title + */ +export const titleRegex = /title([\t ]+([^\n\r]*)|)/; diff --git a/packages/parser/src/language/common/commonValueConverters.ts b/packages/parser/src/language/common/commonValueConverters.ts new file mode 100644 index 000000000..228746c8a --- /dev/null +++ b/packages/parser/src/language/common/commonValueConverters.ts @@ -0,0 +1,74 @@ +import type { CstNode, GrammarAST, ValueType } from 'langium'; +import { DefaultValueConverter } from 'langium'; + +import { accessibilityDescrRegex, accessibilityTitleRegex, titleRegex } from './commonMatcher.js'; + +export class CommonValueConverter extends DefaultValueConverter { + protected override runConverter( + rule: GrammarAST.AbstractRule, + input: string, + cstNode: CstNode + ): ValueType { + const value: ValueType | undefined = CommonValueConverter.customRunConverter( + rule, + input, + cstNode + ); + if (value === undefined) { + return super.runConverter(rule, input, cstNode); + } else { + 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( + rule: GrammarAST.AbstractRule, + input: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _cstNode: CstNode + ): ValueType | undefined { + let regex: RegExp | undefined; + switch (rule.name) { + case 'ACC_DESCR': { + regex = new RegExp(accessibilityDescrRegex.source); + break; + } + case 'ACC_TITLE': { + regex = new RegExp(accessibilityTitleRegex.source); + break; + } + case 'TITLE': { + regex = new RegExp(titleRegex.source); + break; + } + } + if (regex === undefined) { + return undefined; + } + const match = regex.exec(input); + if (match === null) { + return undefined; + } + // single line title, accTitle, accDescr + if (match[1] !== undefined) { + return match[1].trim().replaceAll(/[\t ]{2,}/gm, ' '); + } + // multi line accDescr + if (match[2] !== undefined) { + return match[2] + .replaceAll(/^\s*/gm, '') + .replaceAll(/\s+$/gm, '') + .replaceAll(/[\t ]{2,}/gm, ' ') + .replaceAll(/[\n\r]{2,}/gm, '\n'); + } + return undefined; + } +} diff --git a/packages/parser/src/language/common/index.ts b/packages/parser/src/language/common/index.ts new file mode 100644 index 000000000..554e7902c --- /dev/null +++ b/packages/parser/src/language/common/index.ts @@ -0,0 +1,2 @@ +export * from './commonLexer.js'; +export * from './commonValueConverters.js'; diff --git a/packages/parser/src/language/index.ts b/packages/parser/src/language/index.ts new file mode 100644 index 000000000..c72ed64e5 --- /dev/null +++ b/packages/parser/src/language/index.ts @@ -0,0 +1,5 @@ +export * from './generated/ast.js'; +export * from './generated/grammar.js'; +export * from './generated/module.js'; + +export * from './common/index.js';