From c3bf04b72e259cb91df0d72c6de44cdcbce5f5ea Mon Sep 17 00:00:00 2001 From: Justin Greywolf Date: Tue, 21 Oct 2025 12:12:59 -0700 Subject: [PATCH] Updating grammar to catch additional scenarios --- .../flowchart/parser/flow-arrows.spec.ts | 94 +++++++++++++++++++ .../src/diagrams/flowchart/parser/flow.jison | 15 ++- 2 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 packages/mermaid/src/diagrams/flowchart/parser/flow-arrows.spec.ts diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-arrows.spec.ts b/packages/mermaid/src/diagrams/flowchart/parser/flow-arrows.spec.ts new file mode 100644 index 000000000..084a5bdf8 --- /dev/null +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-arrows.spec.ts @@ -0,0 +1,94 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { parser } from './flow.jison'; +import { FlowDB } from '../flowDb.js'; + +describe('Flowchart arrow parsing - Issue #2492', () => { + let flowDb: FlowDB; + + beforeEach(() => { + flowDb = new FlowDB(); + parser.yy = flowDb; + flowDb.clear(); + }); + + describe('Solid arrows with markers', () => { + it('should parse --> followed by uppercase node', () => { + const diagram = 'graph TD\nA-->B'; + expect(() => parser.parse(diagram)).not.toThrow(); + }); + + it('should parse --> followed by lowercase node', () => { + const diagram = 'graph TD\nA-->b'; + expect(() => parser.parse(diagram)).not.toThrow(); + }); + + it('should parse --> followed by space', () => { + const diagram = 'graph TD\nA--> B'; + expect(() => parser.parse(diagram)).not.toThrow(); + }); + + it('should parse --- followed by uppercase node (issue #2492)', () => { + const diagram = 'graph TD\ndev---Ops'; + expect(() => parser.parse(diagram)).not.toThrow(); + }); + + it('should parse --- followed by lowercase node (issue #2492)', () => { + const diagram = 'graph TD\ndev---ops'; + expect(() => parser.parse(diagram)).not.toThrow(); + }); + + it('should parse --o followed by uppercase node', () => { + const diagram = 'graph TD\nA--oB'; + expect(() => parser.parse(diagram)).not.toThrow(); + }); + + it('should parse --o followed by lowercase node', () => { + const diagram = 'graph TD\nA--ob'; + expect(() => parser.parse(diagram)).not.toThrow(); + }); + + it('should parse --x followed by uppercase node', () => { + const diagram = 'graph TD\nA--xBar'; + expect(() => parser.parse(diagram)).not.toThrow(); + }); + + it('should parse --x followed by lowercase node', () => { + const diagram = 'graph TD\nA--xbar'; + expect(() => parser.parse(diagram)).not.toThrow(); + }); + }); + + describe('Thick arrows with markers', () => { + it('should parse ==> followed by uppercase node', () => { + const diagram = 'graph TD\nA==>B'; + expect(() => parser.parse(diagram)).not.toThrow(); + }); + + it('should parse ==> followed by lowercase node', () => { + const diagram = 'graph TD\nA==>b'; + expect(() => parser.parse(diagram)).not.toThrow(); + }); + + it('should parse === followed by lowercase node', () => { + const diagram = 'graph TD\nA===b'; + expect(() => parser.parse(diagram)).not.toThrow(); + }); + }); + + describe('Dotted arrows with markers', () => { + it('should parse -.-> followed by uppercase node', () => { + const diagram = 'graph TD\nA-.->B'; + expect(() => parser.parse(diagram)).not.toThrow(); + }); + + it('should parse -.-> followed by lowercase node', () => { + const diagram = 'graph TD\nA-.->b'; + expect(() => parser.parse(diagram)).not.toThrow(); + }); + + it('should parse -.- followed by lowercase node', () => { + const diagram = 'graph TD\nA-.-b'; + expect(() => parser.parse(diagram)).not.toThrow(); + }); + }); +}); diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 7b71d6bc4..356efac18 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -154,19 +154,28 @@ that id. \s*[xo<]?\-\-+[xo>]\s+ { this.popState(); return 'LINK'; } \s*[xo<]?\-\-+[xo>](?=[A-Z]) { this.popState(); return 'LINK'; } -\s*[xo<]?\-\-+[-]\s* { this.popState(); return 'LINK'; } +\s*[xo<]?\-\-+[xo>](?=[a-z]) { this.popState(); return 'LINK'; } +\s*[xo<]?\-\-+[-](?=[A-Z]) { this.popState(); return 'LINK'; } +\s*[xo<]?\-\-+[-]\s+ { this.popState(); return 'LINK'; } +\s*[xo<]?\-\-+[-](?=[a-z]) { this.popState(); return 'LINK'; } \s*[xo<]?\-\-\s* { this.pushState("edgeText"); return 'START_LINK'; } [^-]|\-(?!\-)+ return 'EDGE_TEXT'; \s*[xo<]?\=\=+[xo>]\s+ { this.popState(); return 'LINK'; } \s*[xo<]?\=\=+[xo>](?=[A-Z]) { this.popState(); return 'LINK'; } -\s*[xo<]?\=\=+[=]\s* { this.popState(); return 'LINK'; } +\s*[xo<]?\=\=+[xo>](?=[a-z]) { this.popState(); return 'LINK'; } +\s*[xo<]?\=\=+[=](?=[A-Z]) { this.popState(); return 'LINK'; } +\s*[xo<]?\=\=+[=]\s+ { this.popState(); return 'LINK'; } +\s*[xo<]?\=\=+[=](?=[a-z]) { this.popState(); return 'LINK'; } \s*[xo<]?\=\=\s* { this.pushState("thickEdgeText"); return 'START_LINK'; } [^=]|\=(?!=) return 'EDGE_TEXT'; \s*[xo<]?\-?\.+\-[xo>]\s+ { this.popState(); return 'LINK'; } \s*[xo<]?\-?\.+\-[xo>](?=[A-Z]) { this.popState(); return 'LINK'; } -\s*[xo<]?\-?\.+\-\s* { this.popState(); return 'LINK'; } +\s*[xo<]?\-?\.+\-[xo>](?=[a-z]) { this.popState(); return 'LINK'; } +\s*[xo<]?\-?\.+\-(?=[A-Z]) { this.popState(); return 'LINK'; } +\s*[xo<]?\-?\.+\-\s+ { this.popState(); return 'LINK'; } +\s*[xo<]?\-?\.+\-(?=[a-z]) { this.popState(); return 'LINK'; } \s*[xo<]?\-\.\s* { this.pushState("dottedEdgeText"); return 'START_LINK'; } [^\.]|\.(?!-) return 'EDGE_TEXT';