mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-23 18:26:39 +02:00
0 failing
This commit is contained in:
@@ -185,6 +185,67 @@ class LezerFlowParser {
|
|||||||
while (i < tokens.length) {
|
while (i < tokens.length) {
|
||||||
const token = tokens[i];
|
const token = tokens[i];
|
||||||
|
|
||||||
|
// Normalize reserved tokens when used as identifiers or inside identifiers
|
||||||
|
if (token.type === 'DEFAULT') {
|
||||||
|
const prev = processedTokens[processedTokens.length - 1];
|
||||||
|
// Keep DEFAULT only when used in linkStyle (immediately after LINKSTYLE)
|
||||||
|
if (!(prev && prev.type === 'LINKSTYLE')) {
|
||||||
|
processedTokens.push({ ...token, type: 'NODE_STRING', value: 'default' });
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (token.type === 'END') {
|
||||||
|
const prev = processedTokens[processedTokens.length - 1];
|
||||||
|
const next = i + 1 < tokens.length ? tokens[i + 1] : undefined;
|
||||||
|
let merged = false;
|
||||||
|
// Case 1: Merge prev + 'end' when adjacent (no whitespace)
|
||||||
|
if (
|
||||||
|
prev &&
|
||||||
|
(prev.type === 'Identifier' || prev.type === 'NODE_STRING') &&
|
||||||
|
prev.to === token.from
|
||||||
|
) {
|
||||||
|
let newVal = prev.value + 'end';
|
||||||
|
let newTo = token.to;
|
||||||
|
// Also merge next if adjacent and identifier-like
|
||||||
|
if (
|
||||||
|
next &&
|
||||||
|
(next.type === 'Identifier' || next.type === 'NODE_STRING') &&
|
||||||
|
token.to === next.from
|
||||||
|
) {
|
||||||
|
newVal += next.value;
|
||||||
|
newTo = next.to;
|
||||||
|
i += 1; // additionally skip next
|
||||||
|
}
|
||||||
|
// Replace prev with merged NODE_STRING
|
||||||
|
processedTokens[processedTokens.length - 1] = {
|
||||||
|
type: 'NODE_STRING',
|
||||||
|
value: newVal,
|
||||||
|
from: prev.from,
|
||||||
|
to: newTo,
|
||||||
|
} as any;
|
||||||
|
i++;
|
||||||
|
merged = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Case 2: Merge 'end' + next when adjacent (start of identifier)
|
||||||
|
if (
|
||||||
|
next &&
|
||||||
|
(next.type === 'Identifier' || next.type === 'NODE_STRING') &&
|
||||||
|
token.to === next.from
|
||||||
|
) {
|
||||||
|
processedTokens.push({
|
||||||
|
type: 'NODE_STRING',
|
||||||
|
value: 'end' + next.value,
|
||||||
|
from: token.from,
|
||||||
|
to: next.to,
|
||||||
|
} as any);
|
||||||
|
i += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Otherwise, keep END as a standalone token (e.g., subgraph end)
|
||||||
|
}
|
||||||
|
|
||||||
// Skip non-statement tokens
|
// Skip non-statement tokens
|
||||||
if (token.type === 'GRAPH' || token.type === 'DIR' || token.type === 'SEMI') {
|
if (token.type === 'GRAPH' || token.type === 'DIR' || token.type === 'SEMI') {
|
||||||
processedTokens.push(token);
|
processedTokens.push(token);
|
||||||
@@ -1130,6 +1191,19 @@ class LezerFlowParser {
|
|||||||
log.debug(`UIO Processing token ${i}: ${token.type} = "${token.value}"`);
|
log.debug(`UIO Processing token ${i}: ${token.type} = "${token.value}"`);
|
||||||
log.debug(`UIO Processing token ${i}: ${token.type} = "${token.value}"`);
|
log.debug(`UIO Processing token ${i}: ${token.type} = "${token.value}"`);
|
||||||
|
|
||||||
|
// Handle accessibility statements early to avoid misinterpreting them as nodes
|
||||||
|
if (typeof token.value === 'string') {
|
||||||
|
const v = token.value.trim();
|
||||||
|
if (v.startsWith('accTitle')) {
|
||||||
|
i = this.parseAccTitleStatement(tokens, i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (v.startsWith('accDescr')) {
|
||||||
|
i = this.parseAccDescrStatement(tokens, i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (token.type) {
|
switch (token.type) {
|
||||||
case 'GRAPH':
|
case 'GRAPH':
|
||||||
case 'Flowchart':
|
case 'Flowchart':
|
||||||
@@ -1166,7 +1240,23 @@ class LezerFlowParser {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Priority 2: Orphaned shape token for the last referenced node (e.g., A-->B>text])
|
// Disambiguate TagEnd usage: odd-shape start vs continuation arrow head
|
||||||
|
if (token.type === 'TagEnd' && token.value === '>') {
|
||||||
|
const oddAhead = this.isOddShapeAhead(tokens, i);
|
||||||
|
if (oddAhead && this.lastReferencedNodeId) {
|
||||||
|
console.log(
|
||||||
|
`UIO DEBUG: TagEnd '>' detected odd-shape ahead; applying orphaned shape to ${this.lastReferencedNodeId}`
|
||||||
|
);
|
||||||
|
i = this.parseOrphanedShapeStatement(tokens, i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (this.lastTargetNodes.length > 0) {
|
||||||
|
i = this.parseContinuationEdgeStatement(tokens, i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Orphaned shape token for the last referenced node (e.g., A-->B>text])
|
||||||
if (this.isShapeStart(token) && this.lastReferencedNodeId) {
|
if (this.isShapeStart(token) && this.lastReferencedNodeId) {
|
||||||
console.log(
|
console.log(
|
||||||
`UIO DEBUG: Detected orphaned shape token '${token.type}:${token.value}' for lastReferencedNodeId=${this.lastReferencedNodeId}`
|
`UIO DEBUG: Detected orphaned shape token '${token.type}:${token.value}' for lastReferencedNodeId=${this.lastReferencedNodeId}`
|
||||||
@@ -1175,12 +1265,6 @@ class LezerFlowParser {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Priority 3: Continuation edge head (e.g., A-->B-->C)
|
|
||||||
if (token.type === 'TagEnd' && token.value === '>' && this.lastTargetNodes.length > 0) {
|
|
||||||
i = this.parseContinuationEdgeStatement(tokens, i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback: Delegate to parseStatement
|
// Fallback: Delegate to parseStatement
|
||||||
i = this.parseStatement(tokens, i);
|
i = this.parseStatement(tokens, i);
|
||||||
break;
|
break;
|
||||||
@@ -1265,6 +1349,7 @@ class LezerFlowParser {
|
|||||||
i++;
|
i++;
|
||||||
} else if (token.type === 'TagEnd' && token.value === '>') {
|
} else if (token.type === 'TagEnd' && token.value === '>') {
|
||||||
// Handle '>' as LR direction
|
// Handle '>' as LR direction
|
||||||
|
|
||||||
direction = 'LR';
|
direction = 'LR';
|
||||||
i++;
|
i++;
|
||||||
} else if (token.value === '<' || token.value === '^' || token.value === 'v') {
|
} else if (token.value === '<' || token.value === '^' || token.value === 'v') {
|
||||||
@@ -1470,6 +1555,18 @@ class LezerFlowParser {
|
|||||||
return this.parseNodeStatement(tokens, i);
|
return this.parseNodeStatement(tokens, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Standalone class application before an edge: ':::className' applies to last created node
|
||||||
|
if (
|
||||||
|
lookahead.length >= 1 &&
|
||||||
|
lookahead[0].type === 'NODE_STRING' &&
|
||||||
|
lookahead[0].value.startsWith(':::')
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
`UIO DEBUG: Taking node statement path (standalone inline class application detected)`
|
||||||
|
);
|
||||||
|
return this.parseNodeStatement(tokens, i);
|
||||||
|
}
|
||||||
|
|
||||||
// Edge property block: edgeId@{...}
|
// Edge property block: edgeId@{...}
|
||||||
if (
|
if (
|
||||||
lookahead.length >= 3 &&
|
lookahead.length >= 3 &&
|
||||||
@@ -1909,6 +2006,38 @@ class LezerFlowParser {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Heuristic: determine if a TagEnd '>' at index starts an odd-shape (>text])
|
||||||
|
* rather than acting as a split arrow head in chaining (e.g., B-->C --> D).
|
||||||
|
* Looks ahead a short distance: if we see a SquareEnd ']' without encountering
|
||||||
|
* another LINK/Arrow or semicolon, treat it as an odd-shape start.
|
||||||
|
*/
|
||||||
|
private isOddShapeAhead(
|
||||||
|
tokens: { type: string; value: string; from: number; to: number }[],
|
||||||
|
index: number
|
||||||
|
): boolean {
|
||||||
|
// Expect current token to be TagEnd '>'
|
||||||
|
let sawText = false;
|
||||||
|
for (let j = index + 1; j < Math.min(tokens.length, index + 12); j++) {
|
||||||
|
const t = tokens[j];
|
||||||
|
if (t.type === 'SEMI') {
|
||||||
|
return false;
|
||||||
|
} // statement end before shape close
|
||||||
|
if (t.type === 'LINK' || t.type === 'Arrow') {
|
||||||
|
return false;
|
||||||
|
} // likely part of an edge
|
||||||
|
if (t.type === 'SquareEnd') {
|
||||||
|
return sawText;
|
||||||
|
} // found closing ']' after some text
|
||||||
|
if (t.type === 'NODE_STRING' || t.type === 'STR' || t.type === '⚠') {
|
||||||
|
// Consider punctuation/text as content
|
||||||
|
sawText = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a shaped node for a specific target ID (used for embedded arrow patterns)
|
* Parse a shaped node for a specific target ID (used for embedded arrow patterns)
|
||||||
* @param tokens - Array of tokens
|
* @param tokens - Array of tokens
|
||||||
@@ -5029,9 +5158,9 @@ class LezerFlowParser {
|
|||||||
// Title-only case: pass same object for id and title so FlowDB infers generated id
|
// Title-only case: pass same object for id and title so FlowDB infers generated id
|
||||||
if (titleOnly) {
|
if (titleOnly) {
|
||||||
const same = { text: titleText, type: titleType } as const;
|
const same = { text: titleText, type: titleType } as const;
|
||||||
this.yy.addSubGraph(same as any, subgraphDocument, same as any);
|
this.yy.addSubGraph(same as any, subgraphDocument as any, same as any);
|
||||||
} else {
|
} else {
|
||||||
this.yy.addSubGraph({ text: idText }, subgraphDocument, {
|
this.yy.addSubGraph({ text: idText }, subgraphDocument as any, {
|
||||||
text: titleText,
|
text: titleText,
|
||||||
type: titleType,
|
type: titleType,
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user