30 failing

This commit is contained in:
Knut Sveidqvist
2025-08-09 20:34:56 +02:00
parent 1744c82795
commit 933efcfa8c

View File

@@ -249,17 +249,8 @@ class LezerFlowParser {
// 2. A-- text including URL space and send -->B // 2. A-- text including URL space and send -->B
// 3. A---|text|B (pipe-delimited) // 3. A---|text|B (pipe-delimited)
// First, handle the simplest case: a single token containing a complete edge like A---B, A-->B, A--xB // First, check for pipe-delimited pattern (A---|text|B), including fragmented tails like
const single = tokens[startIndex]; // [NODE_STRING 'A', LINK '--', NODE_STRING '-', PIPE '|', ..., PIPE '|', NODE_STRING 'B']
if (single && this.isSimpleEdgePattern(single)) {
const merged = this.mergeSimpleEdgePattern(single as any);
return { mergedTokens: merged, nextIndex: startIndex + 1 };
}
// Defer other merging until after checking for pipe or text-between-arrows
// This ensures patterns like A--text ... -->B are handled as text, not as A -- text.
// Check for pipe-delimited pattern (A---|text|B)
if (this.isPipeDelimitedEdgePattern(tokens, startIndex)) { if (this.isPipeDelimitedEdgePattern(tokens, startIndex)) {
const endIndex = this.findPipeDelimitedPatternEnd(tokens, startIndex); const endIndex = this.findPipeDelimitedPatternEnd(tokens, startIndex);
if (endIndex > startIndex) { if (endIndex > startIndex) {
@@ -278,6 +269,13 @@ class LezerFlowParser {
} }
} }
// Handle the simplest case: a single token containing a complete edge like A---B, A-->B, A--xB
const single = tokens[startIndex];
if (single && this.isSimpleEdgePattern(single)) {
const merged = this.mergeSimpleEdgePattern(single as any);
return { mergedTokens: merged, nextIndex: startIndex + 1 };
}
// Find the end of this potential edge pattern // Find the end of this potential edge pattern
let endIndex = startIndex; let endIndex = startIndex;
let foundArrowEnd = false; let foundArrowEnd = false;
@@ -490,39 +488,65 @@ class LezerFlowParser {
return false; return false;
} }
// First token should be NODE_STRING ending with arrow (like "A---", "A==>", "A-.-") // First token normally is NODE_STRING ending with arrow (like "A---", "A==>", "A-.-").
// However, some inputs tokenize the tail as LINK + NODE_STRING '-' before the first pipe.
// Support both forms.
const first = tokens[0]; const first = tokens[0];
if (first.type !== 'NODE_STRING' || !this.endsWithArrow(first.value)) {
return false;
}
// Second token should be PIPE if (first.type === 'NODE_STRING' && this.endsWithArrow(first.value)) {
if (tokens[1].type !== 'PIPE') { // Case A: Compact form e.g., ["A---", "|", ..., "|", "B"]
return false; if (tokens[1].type !== 'PIPE') {
} return false;
// Find the closing pipe and target
let closingPipeIndex = -1;
for (let i = 2; i < tokens.length; i++) {
if (tokens[i].type === 'PIPE') {
closingPipeIndex = i;
break;
} }
// Find the closing pipe and target
let closingPipeIndex = -1;
for (let i = 2; i < tokens.length; i++) {
if (tokens[i].type === 'PIPE') {
closingPipeIndex = i;
break;
}
}
if (closingPipeIndex === -1 || closingPipeIndex >= tokens.length - 1) {
return false;
}
const last = tokens[tokens.length - 1];
if (last.type !== 'NODE_STRING') {
return false;
}
const textTokens = tokens.slice(2, closingPipeIndex);
return textTokens.every((t) => this.isTextToken(t.type));
} }
if (closingPipeIndex === -1 || closingPipeIndex >= tokens.length - 1) { // Case B: Fragmented tail such as ["A", "--", "-", "|", ..., "|", "B"]
return false; if (
first.type === 'NODE_STRING' &&
tokens.length >= 5 &&
tokens[1].type === 'LINK' &&
(tokens[1].value === '--' || tokens[1].value === '==') &&
tokens[2].type === 'NODE_STRING' &&
tokens[2].value === '-' &&
tokens[3].type === 'PIPE'
) {
// Find the closing pipe and ensure a NODE_STRING target exists
let closingPipeIndex = -1;
for (let i = 4; i < tokens.length; i++) {
if (tokens[i].type === 'PIPE') {
closingPipeIndex = i;
break;
}
}
if (closingPipeIndex === -1 || closingPipeIndex >= tokens.length - 1) {
return false;
}
const last = tokens[tokens.length - 1];
if (last.type !== 'NODE_STRING') {
return false;
}
const textTokens = tokens.slice(4, closingPipeIndex);
return textTokens.every((t) => this.isTextToken(t.type));
} }
// Last token should be NODE_STRING (target) return false;
const last = tokens[tokens.length - 1];
if (last.type !== 'NODE_STRING') {
return false;
}
// All tokens between pipes should be text tokens
const textTokens = tokens.slice(2, closingPipeIndex);
return textTokens.every((t) => this.isTextToken(t.type));
} }
/** /**
@@ -756,24 +780,28 @@ class LezerFlowParser {
* Check if a token type can be treated as text in edge patterns * Check if a token type can be treated as text in edge patterns
*/ */
private isTextToken(tokenType: string): boolean { private isTextToken(tokenType: string): boolean {
// Treat a wide set of tokens as allowable middle text between arrows.
// This matches JISON behavior where many keywords are allowed in edge labels.
return ( return (
tokenType === 'NODE_STRING' || tokenType === 'NODE_STRING' ||
tokenType === 'GRAPH' ||
tokenType === 'DIR' ||
tokenType === 'STR' || tokenType === 'STR' ||
tokenType === 'Flowchart' || tokenType === 'Flowchart' ||
tokenType === 'GRAPH' ||
tokenType === 'DIR' ||
tokenType === 'SUBGRAPH' || tokenType === 'SUBGRAPH' ||
tokenType === 'END' || tokenType === 'END' ||
tokenType === 'STYLE' || tokenType === 'STYLE' ||
tokenType === 'CLASS' || tokenType === 'CLASS' ||
tokenType === 'LINK_TARGET' ||
tokenType === 'CLASSDEF' || tokenType === 'CLASSDEF' ||
tokenType === 'LINKSTYLE' ||
tokenType === 'INTERPOLATE' ||
tokenType === 'DEFAULT' ||
tokenType === 'CLICK' || tokenType === 'CLICK' ||
tokenType === 'HREF' || tokenType === 'HREF' ||
tokenType === 'CALL' || tokenType === 'CALL' ||
tokenType === 'LINKSTYLE' || tokenType === 'LINK_TARGET' ||
tokenType === 'INTERPOLATE' || tokenType === 'SLASH' || // for '/'
tokenType === 'DEFAULT' tokenType === 'BACKTICK' // for '`'
); );
} }
@@ -862,6 +890,15 @@ class LezerFlowParser {
for (let i = 1; i < tokens.length - 1; i++) { for (let i = 1; i < tokens.length - 1; i++) {
const token = tokens[i]; const token = tokens[i];
if (this.isTextToken(token.type)) { if (this.isTextToken(token.type)) {
// Normalize single-character separator tokens into their literal form for text
if (token.type === 'SLASH') {
textParts.push('/');
continue;
}
if (token.type === 'BACKTICK') {
textParts.push('`');
continue;
}
// Check if this is a token that ends with '--' and should have it removed // Check if this is a token that ends with '--' and should have it removed
if (token.value.endsWith('--') && i < tokens.length - 1) { if (token.value.endsWith('--') && i < tokens.length - 1) {
// Check if the next token is TagEnd (>) - this indicates it's the arrow end // Check if the next token is TagEnd (>) - this indicates it's the arrow end
@@ -1412,8 +1449,8 @@ class LezerFlowParser {
hasEmbeddedArrowWithPipe || hasEmbeddedArrowWithPipe ||
hasEmbeddedArrowWithNode) hasEmbeddedArrowWithNode)
) { ) {
console.log( log.debug(
`UIO DEBUG: Taking edge statement path (found edge token in lookahead or split arrow pattern)` `UIO Taking edge statement path (found edge token in lookahead or split arrow pattern)`
); );
return this.parseEdgeStatement(tokens, i); return this.parseEdgeStatement(tokens, i);
} }
@@ -3810,7 +3847,7 @@ class LezerFlowParser {
const closingLink = tokens[i + 1].value; // e.g., '.-', '..-', '...-' const closingLink = tokens[i + 1].value; // e.g., '.-', '..-', '...-'
const targetToken = tokens[i + 2]; const targetToken = tokens[i + 2];
const dotCount = (closingLink.match(/\./g) || []).length; // 1..N const dotCount = (closingLink.match(/\./g) ?? []).length; // 1..N
const targetId = targetToken.value; const targetId = targetToken.value;
i = i + 3; // consume label, closing link, and target i = i + 3; // consume label, closing link, and target
@@ -3826,6 +3863,39 @@ class LezerFlowParser {
}; };
} }
// Special-case: dotted labelled with arrow head like: A -. Label .-> B (length 1..)
// After firstArrow ('-.'), expect: LABEL (NODE_STRING), CLOSING LINK WITH HEAD ('.->','..->','...->'), TARGET
if (
firstArrow === '-.' &&
i + 2 < tokens.length &&
tokens[i].type === 'NODE_STRING' &&
tokens[i + 1].type === 'LINK' &&
/^(\.+)->$/.test(tokens[i + 1].value) &&
(tokens[i + 2].type === 'Identifier' ||
tokens[i + 2].type === 'NODE_STRING' ||
tokens[i + 2].type === 'DIR')
) {
const labelToken = tokens[i];
const closingLinkWithHead = tokens[i + 1].value; // '.->', '..->', ...
const targetToken = tokens[i + 2];
const dotCount = (closingLinkWithHead.match(/\./g) ?? []).length; // 1..N
const targetId = targetToken.value;
i = i + 3; // consume label, closing + head, and target
// Canonicalize to dotted arrow with point head of matching length
const arrowCanonical = `-${'.'.repeat(dotCount)}->`;
return {
arrow: arrowCanonical,
targetId,
text: labelToken.value,
type: 'arrow_point',
stroke: 'dotted',
length: dotCount,
nextIndex: i,
};
}
// Simple arrow pattern: A --> B (but B might be a shaped vertex like B(text)) // Simple arrow pattern: A --> B (but B might be a shaped vertex like B(text))
if ( if (
i >= tokens.length || i >= tokens.length ||