Skip to content

Commit

Permalink
add the approach from mermaid-js#4910 pr
Browse files Browse the repository at this point in the history
  • Loading branch information
Yokozuna59 committed Feb 12, 2024
1 parent c364715 commit 32d82a6
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 23 deletions.
25 changes: 2 additions & 23 deletions packages/parser/src/language/sankey/sankey.langium
Original file line number Diff line number Diff line change
@@ -1,26 +1,5 @@
grammar Sankey

// common stuff in ../common/common.langium
// can't import directly because of SANKEY_LINK_NODE parse rule
interface Common {
accDescr?: string;
accTitle?: string;
title?: string;
}

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 ]+/;
hidden terminal YAML: /---[\t ]*\r?\n[\s\S]*?---[\t ]*(?!.)/;
hidden terminal DIRECTIVE: /[\t ]*%%{[\s\S]*?}%%\s*/;
hidden terminal SINGLE_LINE_COMMENT: /[\t ]*%%[^\n\r]*/;
import "../common/common";

// actual grammar
interface Sankey extends Common {
Expand All @@ -38,7 +17,7 @@ entry Sankey returns Sankey:
;

SankeyLink:
source=SANKEY_LINK_NODE SANKEY_LINK_COMMA target=SANKEY_LINK_NODE SANKEY_LINK_COMMA value=SANKEY_LINK_VALUE NEWLINE+
source=SANKEY_LINK_NODE SANKEY_LINK_COMMA target=SANKEY_LINK_NODE SANKEY_LINK_COMMA value=SANKEY_LINK_VALUE EOL
;

terminal SANKEY_LINK_COMMA: /,/;
Expand Down
44 changes: 44 additions & 0 deletions packages/parser/src/language/sankey/sankeyMatcher.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import type { CustomPatternMatcherFunc } from 'chevrotain';

import { accessibilityDescrRegex, accessibilityTitleRegex, titleRegex } from '../common/matcher.js';
import { matchAnyRegexps } from '../../utils.js';

/**
* Matches sankey link source and target node
*/
Expand All @@ -7,3 +12,42 @@ export const sankeyLinkNodeRegex = /(?:"((?:""|[^"])+)")|([^\n\r,]+(?=%%)|[^\n\r
* Matches sankey link value
*/
export const sankeyLinkValueRegex = /("(?:0|[1-9]\d*)(?:\.\d+)?"|[\t ]*(?:0|[1-9]\d*)(?:\.\d+)?)/;

/**
* Try to match the text with a SankeyNodeLink.
*
* These have to be checked first because they take precedence.
* Langium does not provide any way to specify precedence for grammars (or parts thereof)
* that are `imported` into another grammar definition file (e.g. sankey.langium).
* Specifically, the order of _tokens_ defined in the imported file (e.g. common.langium)
* may or may not come before (or after) the _tokens_ defined in the including file (e.g. sankey.langium).
*
* Thus, we have to manually handle this by first checking for matches that should take precedence
* (in this case title, accessibility title, and accessibility description)
* over matching this token.
*
* First check if the text matches a title, accessibility title, or accessibility description.
* If it matches one of those, return null (no match with a SankeyNodeLink).
*
* If it does not match one of those, then check to see if the text matches the
* SankeyNodeLink Regexp.
*
* Note that _all_ regular expressions have the sticky flag set.
*
* @param text - text to check for matches
* @param startingOffset - offset to start at
*
* @returns Null if there is no match, else return the RegExpExecArray with the match.
*/
export const matchSankeyLinkNode: CustomPatternMatcherFunc = (text, startingOffset) => {
const regexpsToFail: RegExp[] = [accessibilityDescrRegex, accessibilityTitleRegex, titleRegex];
const targetRegexp: RegExp = sankeyLinkNodeRegex;

const matchedOtherPatterns = matchAnyRegexps(text, startingOffset, regexpsToFail);
if (matchedOtherPatterns) {
return null;
}
const stickyTargetRegexp = new RegExp(targetRegexp, targetRegexp.flags + 'y');
stickyTargetRegexp.lastIndex = startingOffset;
return stickyTargetRegexp.exec(text);
};
25 changes: 25 additions & 0 deletions packages/parser/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const STICKY_FLAG = 'y';

/**
* Check to see if text matches any of the given RegExps. Return true as soon
* as there is a match. Use the sticky flag on all RegExps.
*
* @param text - the string to try to match
* @param startingOffset - offset to start at
* @param regexps - a list of RegExps to check for matches
* @internal
*
* @returns true if the text matches any of the RegExps (with the sticky flag set),
* else returns false.
*/
export function matchAnyRegexps(text: string, startingOffset: number, regexps: RegExp[]): boolean {
const found = false;
for (const regexp of regexps) {
const currentRegexp = new RegExp(regexp, regexp.flags + STICKY_FLAG);
currentRegexp.lastIndex = startingOffset;
if (currentRegexp.exec(text) !== null) {
return true;
}
}
return found;
}

0 comments on commit 32d82a6

Please sign in to comment.