sirena 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/workflows/build_deploy.yml +59 -0
- data/.github/workflows/links.yml +85 -0
- data/.github/workflows/rake.yml +15 -0
- data/.github/workflows/release.yml +27 -0
- data/.gitignore +68 -0
- data/.rspec +3 -0
- data/.rubocop.yml +14 -0
- data/.rubocop_todo.yml +70 -0
- data/ARCHITECTURE.md +744 -0
- data/Gemfile +12 -0
- data/LICENSE +25 -0
- data/README.adoc +357 -0
- data/Rakefile +11 -0
- data/docs/.gitignore +1 -0
- data/docs/Gemfile +13 -0
- data/docs/_config.yml +182 -0
- data/docs/_diagram_types/architecture-diagram.adoc +314 -0
- data/docs/_diagram_types/block-diagram.adoc +345 -0
- data/docs/_diagram_types/c4-diagram.adoc +559 -0
- data/docs/_diagram_types/class-diagram.adoc +816 -0
- data/docs/_diagram_types/er-diagram.adoc +719 -0
- data/docs/_diagram_types/error-diagram.adoc +114 -0
- data/docs/_diagram_types/examples/flowchart-examples.adoc +29 -0
- data/docs/_diagram_types/flowchart.adoc +488 -0
- data/docs/_diagram_types/gantt-chart.adoc +502 -0
- data/docs/_diagram_types/git-graph.adoc +600 -0
- data/docs/_diagram_types/index.adoc +192 -0
- data/docs/_diagram_types/info-diagram.adoc +103 -0
- data/docs/_diagram_types/kanban-diagram.adoc +262 -0
- data/docs/_diagram_types/mindmap.adoc +603 -0
- data/docs/_diagram_types/packet-diagram.adoc +378 -0
- data/docs/_diagram_types/pie-chart.adoc +335 -0
- data/docs/_diagram_types/quadrant-chart.adoc +406 -0
- data/docs/_diagram_types/radar-chart.adoc +528 -0
- data/docs/_diagram_types/requirement-diagram.adoc +416 -0
- data/docs/_diagram_types/sankey-diagram.adoc +357 -0
- data/docs/_diagram_types/sequence-diagram.adoc +664 -0
- data/docs/_diagram_types/state-diagram.adoc +658 -0
- data/docs/_diagram_types/timeline.adoc +352 -0
- data/docs/_diagram_types/treemap-diagram.adoc +462 -0
- data/docs/_diagram_types/user-journey.adoc +602 -0
- data/docs/_features/index.adoc +129 -0
- data/docs/_guides/cli-reference.adoc +203 -0
- data/docs/_guides/index.adoc +56 -0
- data/docs/_guides/installation.adoc +100 -0
- data/docs/_guides/quick-start.adoc +132 -0
- data/docs/_pages/comparison.adoc +441 -0
- data/docs/_pages/compatibility.adoc +300 -0
- data/docs/_pages/index.adoc +39 -0
- data/docs/_references/index.adoc +103 -0
- data/docs/_tutorials/index.adoc +57 -0
- data/docs/index.adoc +166 -0
- data/docs/lychee.toml +54 -0
- data/examples/.gitignore +10 -0
- data/examples/README.adoc +196 -0
- data/examples/README.md +64 -0
- data/examples/architecture/01-basic-services.mmd +9 -0
- data/examples/architecture/01-basic-services.svg +37 -0
- data/examples/architecture/02-service-groups.mmd +16 -0
- data/examples/architecture/02-service-groups.svg +55 -0
- data/examples/architecture/README.adoc +79 -0
- data/examples/block/01-basic-blocks.mmd +13 -0
- data/examples/block/01-basic-blocks.svg +44 -0
- data/examples/block/02-block-shapes.mmd +13 -0
- data/examples/block/02-block-shapes.svg +47 -0
- data/examples/block/README.adoc +85 -0
- data/examples/c4/01-context-diagram.mmd +10 -0
- data/examples/c4/01-context-diagram.svg +45 -0
- data/examples/c4/02-container-diagram.mmd +24 -0
- data/examples/c4/02-container-diagram.svg +105 -0
- data/examples/c4/README.adoc +92 -0
- data/examples/class_diagram/01-basic-classes.mmd +61 -0
- data/examples/class_diagram/01-basic-classes.svg +117 -0
- data/examples/class_diagram/02-relationships.mmd +61 -0
- data/examples/class_diagram/02-relationships.svg +129 -0
- data/examples/class_diagram/README.adoc +93 -0
- data/examples/er_diagram/01-basic-entities.mmd +64 -0
- data/examples/er_diagram/01-basic-entities.svg +5 -0
- data/examples/er_diagram/02-cardinality.mmd +57 -0
- data/examples/er_diagram/02-cardinality.svg +125 -0
- data/examples/er_diagram/README.adoc +88 -0
- data/examples/error/01-basic-error.mmd +1 -0
- data/examples/error/01-basic-error.svg +13 -0
- data/examples/error/02-error-display.mmd +1 -0
- data/examples/error/02-error-display.svg +13 -0
- data/examples/error/README.adoc +71 -0
- data/examples/error_message_example.svg +13 -0
- data/examples/flowchart/00-original.mmd +13 -0
- data/examples/flowchart/00-original.svg +5 -0
- data/examples/flowchart/01-basic-flow.mmd +7 -0
- data/examples/flowchart/01-basic-flow.svg +52 -0
- data/examples/flowchart/01-basic-flow.yml +13 -0
- data/examples/flowchart/02*.svg +87 -0
- data/examples/flowchart/02-node-shapes.mmd +9 -0
- data/examples/flowchart/02-node-shapes.svg +33 -0
- data/examples/flowchart/03-edge-types.mmd +7 -0
- data/examples/flowchart/03-edge-types.svg +53 -0
- data/examples/flowchart/04-subgraphs.mmd +9 -0
- data/examples/flowchart/04-subgraphs.svg +33 -0
- data/examples/flowchart/05-styling.mmd +9 -0
- data/examples/flowchart/05-styling.svg +33 -0
- data/examples/flowchart/06-complex-flow.mmd +8 -0
- data/examples/flowchart/06-complex-flow.svg +59 -0
- data/examples/flowchart/README.adoc +167 -0
- data/examples/gantt/01-simple-timeline.* +14 -0
- data/examples/gantt/01-simple-timeline.mmd +6 -0
- data/examples/gantt/01-simple-timeline.svg +26 -0
- data/examples/gantt/02-task-dependencies.mmd +6 -0
- data/examples/gantt/02-task-dependencies.svg +26 -0
- data/examples/gantt/README.adoc +86 -0
- data/examples/git_graph/01-linear-history.mmd +12 -0
- data/examples/git_graph/01-linear-history.svg +26 -0
- data/examples/git_graph/02-branching.mmd +12 -0
- data/examples/git_graph/02-branching.svg +26 -0
- data/examples/git_graph/README.adoc +73 -0
- data/examples/info/02-showinfo.mmd +1 -0
- data/examples/info/02-showinfo.svg +10 -0
- data/examples/info/README.adoc +58 -0
- data/examples/info_showinfo_example.svg +10 -0
- data/examples/kanban/01-simple-board.mmd +8 -0
- data/examples/kanban/01-simple-board.svg +43 -0
- data/examples/kanban/02-workflow.mmd +8 -0
- data/examples/kanban/02-workflow.svg +43 -0
- data/examples/kanban/README.adoc +79 -0
- data/examples/mindmap/01-simple-tree.mmd +19 -0
- data/examples/mindmap/01-simple-tree.svg +61 -0
- data/examples/mindmap/02-knowledge-map.mmd +19 -0
- data/examples/mindmap/02-knowledge-map.svg +61 -0
- data/examples/mindmap/README.adoc +77 -0
- data/examples/packet/01-basic-packet.* +17 -0
- data/examples/packet/01-basic-packet.mmd +4 -0
- data/examples/packet/01-basic-packet.svg +82 -0
- data/examples/packet/README.adoc +58 -0
- data/examples/pie/01-simple-chart.mmd +5 -0
- data/examples/pie/01-simple-chart.svg +17 -0
- data/examples/pie/02-labeled-slices.mmd +6 -0
- data/examples/pie/02-labeled-slices.svg +19 -0
- data/examples/pie/README.adoc +75 -0
- data/examples/quadrant/01-basic-quadrant.mmd +13 -0
- data/examples/quadrant/01-basic-quadrant.svg +33 -0
- data/examples/quadrant/02-positioned-items.mmd +14 -0
- data/examples/quadrant/02-positioned-items.svg +35 -0
- data/examples/quadrant/README.adoc +84 -0
- data/examples/radar/01-simple-radar.* +5 -0
- data/examples/radar/01-simple-radar.mmd +3 -0
- data/examples/radar/01-simple-radar.svg +25 -0
- data/examples/radar/02-multiple-curves.mmd +4 -0
- data/examples/radar/02-multiple-curves.svg +43 -0
- data/examples/radar/README.adoc +75 -0
- data/examples/requirement/01-basic-requirements.mmd +23 -0
- data/examples/requirement/01-basic-requirements.svg +49 -0
- data/examples/requirement/02-risk-levels.mmd +23 -0
- data/examples/requirement/02-risk-levels.svg +49 -0
- data/examples/requirement/README.adoc +85 -0
- data/examples/sankey/01-simple-flow.mmd +7 -0
- data/examples/sankey/01-simple-flow.svg +34 -0
- data/examples/sankey/02-multi-stage.mmd +11 -0
- data/examples/sankey/02-multi-stage.svg +44 -0
- data/examples/sankey/README.adoc +74 -0
- data/examples/sequence/01-basic-sequence.mmd +27 -0
- data/examples/sequence/01-basic-sequence.svg +5 -0
- data/examples/sequence/02-activations.mmd +17 -0
- data/examples/sequence/02-activations.svg +78 -0
- data/examples/sequence/README.adoc +86 -0
- data/examples/state_diagram/01-simple-states.mmd +29 -0
- data/examples/state_diagram/01-simple-states.svg +5 -0
- data/examples/state_diagram/02-composite.mmd +19 -0
- data/examples/state_diagram/02-composite.svg +81 -0
- data/examples/state_diagram/README.adoc +90 -0
- data/examples/timeline/01-simple-timeline.mmd +11 -0
- data/examples/timeline/01-simple-timeline.svg +36 -0
- data/examples/timeline/02-periods.mmd +15 -0
- data/examples/timeline/02-periods.svg +47 -0
- data/examples/timeline/README.adoc +78 -0
- data/examples/treemap/01-basic-treemap.mmd +12 -0
- data/examples/treemap/01-basic-treemap.svg +59 -0
- data/examples/treemap/README.adoc +59 -0
- data/examples/user_journey/01-simple-journey.mmd +23 -0
- data/examples/user_journey/01-simple-journey.svg +5 -0
- data/examples/user_journey/02-multi-actor.mmd +18 -0
- data/examples/user_journey/02-multi-actor.svg +129 -0
- data/examples/user_journey/README.adoc +81 -0
- data/examples/xychart/01-line-chart.mmd +5 -0
- data/examples/xychart/01-line-chart.svg +43 -0
- data/examples/xychart/02-bar-chart.mmd +7 -0
- data/examples/xychart/02-bar-chart.svg +48 -0
- data/examples/xychart/README.adoc +80 -0
- data/exe/sirena +7 -0
- data/lib/sirena/cli.rb +138 -0
- data/lib/sirena/commands/batch.rb +117 -0
- data/lib/sirena/commands/render.rb +80 -0
- data/lib/sirena/commands/types.rb +29 -0
- data/lib/sirena/commands/version.rb +24 -0
- data/lib/sirena/diagram/architecture.rb +46 -0
- data/lib/sirena/diagram/base.rb +61 -0
- data/lib/sirena/diagram/block.rb +81 -0
- data/lib/sirena/diagram/c4.rb +328 -0
- data/lib/sirena/diagram/class_diagram.rb +385 -0
- data/lib/sirena/diagram/er_diagram.rb +238 -0
- data/lib/sirena/diagram/error.rb +38 -0
- data/lib/sirena/diagram/flowchart.rb +160 -0
- data/lib/sirena/diagram/gantt.rb +71 -0
- data/lib/sirena/diagram/git_graph.rb +36 -0
- data/lib/sirena/diagram/info.rb +38 -0
- data/lib/sirena/diagram/kanban.rb +178 -0
- data/lib/sirena/diagram/mindmap.rb +54 -0
- data/lib/sirena/diagram/packet.rb +79 -0
- data/lib/sirena/diagram/pie.rb +115 -0
- data/lib/sirena/diagram/quadrant.rb +138 -0
- data/lib/sirena/diagram/radar.rb +52 -0
- data/lib/sirena/diagram/requirement.rb +133 -0
- data/lib/sirena/diagram/sankey.rb +217 -0
- data/lib/sirena/diagram/sequence.rb +242 -0
- data/lib/sirena/diagram/state_diagram.rb +237 -0
- data/lib/sirena/diagram/timeline.rb +171 -0
- data/lib/sirena/diagram/treemap.rb +84 -0
- data/lib/sirena/diagram/user_journey.rb +149 -0
- data/lib/sirena/diagram/xy_chart.rb +76 -0
- data/lib/sirena/diagram.rb +8 -0
- data/lib/sirena/diagram_registry.rb +101 -0
- data/lib/sirena/engine.rb +292 -0
- data/lib/sirena/parser/architecture.rb +41 -0
- data/lib/sirena/parser/base.rb +41 -0
- data/lib/sirena/parser/block.rb +72 -0
- data/lib/sirena/parser/c4.rb +53 -0
- data/lib/sirena/parser/class_diagram.rb +63 -0
- data/lib/sirena/parser/er_diagram.rb +40 -0
- data/lib/sirena/parser/error.rb +49 -0
- data/lib/sirena/parser/flowchart.rb +71 -0
- data/lib/sirena/parser/gantt.rb +60 -0
- data/lib/sirena/parser/git_graph.rb +95 -0
- data/lib/sirena/parser/grammars/architecture.rb +145 -0
- data/lib/sirena/parser/grammars/block.rb +190 -0
- data/lib/sirena/parser/grammars/c4.rb +226 -0
- data/lib/sirena/parser/grammars/class_diagram.rb +284 -0
- data/lib/sirena/parser/grammars/common.rb +84 -0
- data/lib/sirena/parser/grammars/er_diagram.rb +114 -0
- data/lib/sirena/parser/grammars/error.rb +40 -0
- data/lib/sirena/parser/grammars/flowchart.rb +298 -0
- data/lib/sirena/parser/grammars/gantt.rb +252 -0
- data/lib/sirena/parser/grammars/git_graph.rb +167 -0
- data/lib/sirena/parser/grammars/info.rb +58 -0
- data/lib/sirena/parser/grammars/kanban.rb +83 -0
- data/lib/sirena/parser/grammars/mindmap.rb +115 -0
- data/lib/sirena/parser/grammars/packet.rb +73 -0
- data/lib/sirena/parser/grammars/pie.rb +128 -0
- data/lib/sirena/parser/grammars/quadrant.rb +199 -0
- data/lib/sirena/parser/grammars/radar.rb +150 -0
- data/lib/sirena/parser/grammars/requirement.rb +188 -0
- data/lib/sirena/parser/grammars/sankey.rb +104 -0
- data/lib/sirena/parser/grammars/sequence.rb +247 -0
- data/lib/sirena/parser/grammars/state_diagram.rb +172 -0
- data/lib/sirena/parser/grammars/timeline.rb +142 -0
- data/lib/sirena/parser/grammars/treemap.rb +120 -0
- data/lib/sirena/parser/grammars/xy_chart.rb +120 -0
- data/lib/sirena/parser/info.rb +49 -0
- data/lib/sirena/parser/kanban.rb +97 -0
- data/lib/sirena/parser/mindmap.rb +106 -0
- data/lib/sirena/parser/packet.rb +76 -0
- data/lib/sirena/parser/pie.rb +49 -0
- data/lib/sirena/parser/quadrant.rb +57 -0
- data/lib/sirena/parser/radar.rb +104 -0
- data/lib/sirena/parser/requirement.rb +70 -0
- data/lib/sirena/parser/sankey.rb +64 -0
- data/lib/sirena/parser/sequence.rb +51 -0
- data/lib/sirena/parser/state_diagram.rb +69 -0
- data/lib/sirena/parser/timeline.rb +57 -0
- data/lib/sirena/parser/transforms/architecture.rb +97 -0
- data/lib/sirena/parser/transforms/block.rb +254 -0
- data/lib/sirena/parser/transforms/c4.rb +347 -0
- data/lib/sirena/parser/transforms/class_diagram.rb +352 -0
- data/lib/sirena/parser/transforms/er_diagram.rb +169 -0
- data/lib/sirena/parser/transforms/error.rb +58 -0
- data/lib/sirena/parser/transforms/flowchart.rb +293 -0
- data/lib/sirena/parser/transforms/gantt.rb +215 -0
- data/lib/sirena/parser/transforms/git_graph.rb +160 -0
- data/lib/sirena/parser/transforms/info.rb +58 -0
- data/lib/sirena/parser/transforms/kanban.rb +176 -0
- data/lib/sirena/parser/transforms/mindmap.rb +227 -0
- data/lib/sirena/parser/transforms/packet.rb +63 -0
- data/lib/sirena/parser/transforms/pie.rb +143 -0
- data/lib/sirena/parser/transforms/quadrant.rb +177 -0
- data/lib/sirena/parser/transforms/radar.rb +126 -0
- data/lib/sirena/parser/transforms/requirement.rb +272 -0
- data/lib/sirena/parser/transforms/sankey.rb +122 -0
- data/lib/sirena/parser/transforms/sequence.rb +342 -0
- data/lib/sirena/parser/transforms/state_diagram.rb +292 -0
- data/lib/sirena/parser/transforms/timeline.rb +177 -0
- data/lib/sirena/parser/transforms/treemap.rb +81 -0
- data/lib/sirena/parser/transforms/xy_chart.rb +132 -0
- data/lib/sirena/parser/treemap.rb +98 -0
- data/lib/sirena/parser/user_journey.rb +120 -0
- data/lib/sirena/parser/xy_chart.rb +114 -0
- data/lib/sirena/parser.rb +8 -0
- data/lib/sirena/renderer/architecture.rb +251 -0
- data/lib/sirena/renderer/base.rb +251 -0
- data/lib/sirena/renderer/block.rb +286 -0
- data/lib/sirena/renderer/c4.rb +490 -0
- data/lib/sirena/renderer/class_diagram.rb +499 -0
- data/lib/sirena/renderer/er_diagram.rb +417 -0
- data/lib/sirena/renderer/error.rb +131 -0
- data/lib/sirena/renderer/flowchart.rb +301 -0
- data/lib/sirena/renderer/gantt.rb +331 -0
- data/lib/sirena/renderer/git_graph.rb +368 -0
- data/lib/sirena/renderer/info.rb +93 -0
- data/lib/sirena/renderer/kanban.rb +295 -0
- data/lib/sirena/renderer/mindmap.rb +396 -0
- data/lib/sirena/renderer/packet.rb +239 -0
- data/lib/sirena/renderer/pie.rb +235 -0
- data/lib/sirena/renderer/quadrant.rb +292 -0
- data/lib/sirena/renderer/radar.rb +323 -0
- data/lib/sirena/renderer/requirement.rb +371 -0
- data/lib/sirena/renderer/sankey.rb +255 -0
- data/lib/sirena/renderer/sequence.rb +424 -0
- data/lib/sirena/renderer/state_diagram.rb +328 -0
- data/lib/sirena/renderer/timeline.rb +304 -0
- data/lib/sirena/renderer/treemap.rb +152 -0
- data/lib/sirena/renderer/user_journey.rb +331 -0
- data/lib/sirena/renderer/xy_chart.rb +452 -0
- data/lib/sirena/renderer.rb +8 -0
- data/lib/sirena/svg/circle.rb +41 -0
- data/lib/sirena/svg/document.rb +103 -0
- data/lib/sirena/svg/element.rb +65 -0
- data/lib/sirena/svg/ellipse.rb +33 -0
- data/lib/sirena/svg/group.rb +71 -0
- data/lib/sirena/svg/line.rb +49 -0
- data/lib/sirena/svg/path.rb +76 -0
- data/lib/sirena/svg/polygon.rb +43 -0
- data/lib/sirena/svg/polyline.rb +35 -0
- data/lib/sirena/svg/rect.rb +57 -0
- data/lib/sirena/svg/style.rb +44 -0
- data/lib/sirena/svg/text.rb +72 -0
- data/lib/sirena/svg.rb +19 -0
- data/lib/sirena/text_measurement.rb +71 -0
- data/lib/sirena/theme/builtin/dark.yml +70 -0
- data/lib/sirena/theme/builtin/default.yml +80 -0
- data/lib/sirena/theme/builtin/high_contrast.yml +70 -0
- data/lib/sirena/theme/builtin/light.yml +70 -0
- data/lib/sirena/theme/color_palette.rb +48 -0
- data/lib/sirena/theme/effect_styles.rb +28 -0
- data/lib/sirena/theme/registry.rb +41 -0
- data/lib/sirena/theme/shape_styles.rb +28 -0
- data/lib/sirena/theme/spacing_config.rb +24 -0
- data/lib/sirena/theme/typography.rb +30 -0
- data/lib/sirena/theme.rb +69 -0
- data/lib/sirena/transform/architecture.rb +273 -0
- data/lib/sirena/transform/base.rb +199 -0
- data/lib/sirena/transform/block.rb +215 -0
- data/lib/sirena/transform/c4.rb +288 -0
- data/lib/sirena/transform/class_diagram.rb +296 -0
- data/lib/sirena/transform/er_diagram.rb +204 -0
- data/lib/sirena/transform/error.rb +39 -0
- data/lib/sirena/transform/flowchart.rb +161 -0
- data/lib/sirena/transform/gantt.rb +253 -0
- data/lib/sirena/transform/git_graph.rb +283 -0
- data/lib/sirena/transform/info.rb +39 -0
- data/lib/sirena/transform/kanban.rb +180 -0
- data/lib/sirena/transform/mindmap.rb +251 -0
- data/lib/sirena/transform/packet.rb +185 -0
- data/lib/sirena/transform/pie.rb +62 -0
- data/lib/sirena/transform/quadrant.rb +167 -0
- data/lib/sirena/transform/radar.rb +227 -0
- data/lib/sirena/transform/requirement.rb +233 -0
- data/lib/sirena/transform/sankey.rb +212 -0
- data/lib/sirena/transform/sequence.rb +143 -0
- data/lib/sirena/transform/state_diagram.rb +228 -0
- data/lib/sirena/transform/timeline.rb +139 -0
- data/lib/sirena/transform/treemap.rb +120 -0
- data/lib/sirena/transform/user_journey.rb +207 -0
- data/lib/sirena/transform/xy_chart.rb +273 -0
- data/lib/sirena/transform.rb +8 -0
- data/lib/sirena/version.rb +5 -0
- data/lib/sirena.rb +328 -0
- data/lib/tasks/benchmark.rake +532 -0
- data/lib/tasks/examples.rake +468 -0
- data/lib/tasks/generate_mermaid_fixtures.rake +363 -0
- data/lib/tasks/mermaid_fixtures.rake +46 -0
- data/scripts/extract_mermaid_tests.rb +493 -0
- data/scripts/rename_to_sirena.rb +73 -0
- data/sirena.gemspec +47 -0
- metadata +529 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
require_relative "grammars/mindmap"
|
|
5
|
+
require_relative "transforms/mindmap"
|
|
6
|
+
require_relative "../diagram/mindmap"
|
|
7
|
+
|
|
8
|
+
module Sirena
|
|
9
|
+
module Parser
|
|
10
|
+
# Mindmap parser for Mermaid mindmap diagram syntax.
|
|
11
|
+
#
|
|
12
|
+
# Uses Parslet grammar-based parsing to handle mindmap syntax
|
|
13
|
+
# with hierarchical nodes, shapes, icons, and classes.
|
|
14
|
+
#
|
|
15
|
+
# Parses mindmaps with support for:
|
|
16
|
+
# - Indentation-based hierarchy
|
|
17
|
+
# - Multiple node shapes (circle, cloud, bang, hexagon, square)
|
|
18
|
+
# - Icons (::icon(name))
|
|
19
|
+
# - Classes (:::className)
|
|
20
|
+
#
|
|
21
|
+
# @example Parse a simple mindmap
|
|
22
|
+
# parser = MindmapParser.new
|
|
23
|
+
# source = <<~MERMAID
|
|
24
|
+
# mindmap
|
|
25
|
+
# root((Central Idea))
|
|
26
|
+
# Branch 1
|
|
27
|
+
# Sub-item 1.1
|
|
28
|
+
# Branch 2
|
|
29
|
+
# MERMAID
|
|
30
|
+
# diagram = parser.parse(source)
|
|
31
|
+
class MindmapParser < Base
|
|
32
|
+
# Parses mindmap diagram source into a Mindmap model.
|
|
33
|
+
#
|
|
34
|
+
# @param source [String] the Mermaid mindmap diagram source
|
|
35
|
+
# @return [Diagram::Mindmap] the parsed mindmap diagram
|
|
36
|
+
# @raise [ParseError] if syntax is invalid
|
|
37
|
+
def parse(source)
|
|
38
|
+
grammar = Grammars::Mindmap.new
|
|
39
|
+
|
|
40
|
+
begin
|
|
41
|
+
parse_tree = grammar.parse(source)
|
|
42
|
+
rescue Parslet::ParseFailed => e
|
|
43
|
+
raise ParseError, "Syntax error at #{e.parse_failure_cause.pos}: " \
|
|
44
|
+
"#{e.parse_failure_cause}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Transform parse tree to diagram model
|
|
48
|
+
transform = Transforms::Mindmap.new
|
|
49
|
+
result = transform.apply(parse_tree)
|
|
50
|
+
|
|
51
|
+
# Create the diagram model
|
|
52
|
+
create_diagram(result)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def create_diagram(result)
|
|
58
|
+
diagram = Diagram::Mindmap.new
|
|
59
|
+
|
|
60
|
+
# Build node hierarchy
|
|
61
|
+
if result[:root]
|
|
62
|
+
root = build_node(result[:root])
|
|
63
|
+
diagram.root = root
|
|
64
|
+
diagram.add_node(root)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Add all nodes to diagram
|
|
68
|
+
result[:nodes]&.each do |node_data|
|
|
69
|
+
next if node_data == result[:root]
|
|
70
|
+
node = find_or_create_node(diagram, node_data)
|
|
71
|
+
diagram.add_node(node) unless diagram.nodes.include?(node)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
diagram
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def build_node(node_data, parent = nil)
|
|
78
|
+
node = Diagram::Mindmap::MindmapNode.new(
|
|
79
|
+
id: node_data[:id],
|
|
80
|
+
content: node_data[:content],
|
|
81
|
+
level: node_data[:level],
|
|
82
|
+
shape: node_data[:shape] || "default",
|
|
83
|
+
icon: node_data[:icon],
|
|
84
|
+
classes: node_data[:classes] || []
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
node.parent = parent if parent
|
|
88
|
+
|
|
89
|
+
# Build children recursively
|
|
90
|
+
node_data[:children]&.each do |child_data|
|
|
91
|
+
child = build_node(child_data, node)
|
|
92
|
+
node.add_child(child)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
node
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def find_or_create_node(diagram, node_data)
|
|
99
|
+
existing = diagram.nodes.find { |n| n.id == node_data[:id] }
|
|
100
|
+
return existing if existing
|
|
101
|
+
|
|
102
|
+
build_node(node_data)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
require_relative "grammars/packet"
|
|
5
|
+
require_relative "transforms/packet"
|
|
6
|
+
require_relative "../diagram/packet"
|
|
7
|
+
|
|
8
|
+
module Sirena
|
|
9
|
+
module Parser
|
|
10
|
+
# Packet diagram parser for Mermaid packet-beta diagram syntax.
|
|
11
|
+
#
|
|
12
|
+
# Uses Parslet grammar-based parsing to handle packet diagram syntax
|
|
13
|
+
# with bit ranges and field labels.
|
|
14
|
+
#
|
|
15
|
+
# Parses packet diagrams with support for:
|
|
16
|
+
# - Title metadata
|
|
17
|
+
# - Field definitions with bit ranges
|
|
18
|
+
# - Field labels
|
|
19
|
+
#
|
|
20
|
+
# @example Parse a simple packet diagram
|
|
21
|
+
# parser = PacketParser.new
|
|
22
|
+
# source = <<~MERMAID
|
|
23
|
+
# packet-beta
|
|
24
|
+
# title Hello world
|
|
25
|
+
# 0-10: "hello"
|
|
26
|
+
# MERMAID
|
|
27
|
+
# diagram = parser.parse(source)
|
|
28
|
+
class PacketParser < Base
|
|
29
|
+
# Parses packet diagram source into a PacketDiagram model.
|
|
30
|
+
#
|
|
31
|
+
# @param source [String] the Mermaid packet diagram source
|
|
32
|
+
# @return [Diagram::PacketDiagram] the parsed packet diagram
|
|
33
|
+
# @raise [ParseError] if syntax is invalid
|
|
34
|
+
def parse(source)
|
|
35
|
+
grammar = Grammars::Packet.new
|
|
36
|
+
|
|
37
|
+
begin
|
|
38
|
+
parse_tree = grammar.parse(source)
|
|
39
|
+
rescue Parslet::ParseFailed => e
|
|
40
|
+
raise ParseError, "Syntax error at #{e.parse_failure_cause.pos}: " \
|
|
41
|
+
"#{e.parse_failure_cause}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Transform parse tree to intermediate representation
|
|
45
|
+
transform = Transforms::Packet.new
|
|
46
|
+
result = transform.apply(parse_tree)
|
|
47
|
+
|
|
48
|
+
# Create the diagram model
|
|
49
|
+
create_diagram(result)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def create_diagram(result)
|
|
55
|
+
diagram = Diagram::PacketDiagram.new
|
|
56
|
+
|
|
57
|
+
# Handle case where result is not a hash (empty diagram)
|
|
58
|
+
return diagram unless result.is_a?(Hash)
|
|
59
|
+
|
|
60
|
+
diagram.title = result[:title]
|
|
61
|
+
|
|
62
|
+
# Create fields
|
|
63
|
+
Array(result[:fields]).each do |field_data|
|
|
64
|
+
field = Diagram::PacketField.new(
|
|
65
|
+
field_data[:bit_start],
|
|
66
|
+
field_data[:bit_end],
|
|
67
|
+
field_data[:label]
|
|
68
|
+
)
|
|
69
|
+
diagram.add_field(field)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
diagram
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
require_relative 'grammars/pie'
|
|
5
|
+
require_relative 'transforms/pie'
|
|
6
|
+
require_relative '../diagram/pie'
|
|
7
|
+
|
|
8
|
+
module Sirena
|
|
9
|
+
module Parser
|
|
10
|
+
# Pie chart parser for Mermaid pie diagram syntax.
|
|
11
|
+
#
|
|
12
|
+
# Uses Parslet grammar-based parsing to handle pie chart syntax
|
|
13
|
+
# with labeled data entries and numeric values.
|
|
14
|
+
#
|
|
15
|
+
# Parses pie charts with support for:
|
|
16
|
+
# - Title declarations
|
|
17
|
+
# - showData flag for displaying values
|
|
18
|
+
# - Data entries with quoted labels and numeric values
|
|
19
|
+
# - Accessibility features (accTitle, accDescr)
|
|
20
|
+
# - Comments
|
|
21
|
+
#
|
|
22
|
+
# @example Parse a simple pie chart
|
|
23
|
+
# parser = PieParser.new
|
|
24
|
+
# diagram = parser.parse("pie\n \"Apples\" : 42\n \"Oranges\" : 58")
|
|
25
|
+
class PieParser < Base
|
|
26
|
+
# Parses pie chart diagram source into a Pie diagram model.
|
|
27
|
+
#
|
|
28
|
+
# @param source [String] the Mermaid pie chart diagram source
|
|
29
|
+
# @return [Diagram::Pie] the parsed pie chart diagram
|
|
30
|
+
# @raise [ParseError] if syntax is invalid
|
|
31
|
+
def parse(source)
|
|
32
|
+
grammar = Grammars::Pie.new
|
|
33
|
+
|
|
34
|
+
begin
|
|
35
|
+
parse_tree = grammar.parse(source)
|
|
36
|
+
rescue Parslet::ParseFailed => e
|
|
37
|
+
raise ParseError, "Syntax error at #{e.parse_failure_cause.pos}: " \
|
|
38
|
+
"#{e.parse_failure_cause}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Transform parse tree to diagram model
|
|
42
|
+
transform = Transforms::Pie.new
|
|
43
|
+
diagram = transform.apply(parse_tree)
|
|
44
|
+
|
|
45
|
+
diagram
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
require_relative 'grammars/quadrant'
|
|
5
|
+
require_relative 'transforms/quadrant'
|
|
6
|
+
require_relative '../diagram/quadrant'
|
|
7
|
+
|
|
8
|
+
module Sirena
|
|
9
|
+
module Parser
|
|
10
|
+
# Quadrant chart parser for Mermaid quadrant diagram syntax.
|
|
11
|
+
#
|
|
12
|
+
# Uses Parslet grammar-based parsing to handle quadrant chart syntax
|
|
13
|
+
# with axis labels, quadrant labels, and data points.
|
|
14
|
+
#
|
|
15
|
+
# Parses quadrant charts with support for:
|
|
16
|
+
# - Title declarations
|
|
17
|
+
# - X-axis and Y-axis labels
|
|
18
|
+
# - Quadrant labels (1-4)
|
|
19
|
+
# - Data points with normalized coordinates
|
|
20
|
+
# - Point styling (radius, color, stroke)
|
|
21
|
+
# - Comments
|
|
22
|
+
#
|
|
23
|
+
# @example Parse a simple quadrant chart
|
|
24
|
+
# parser = QuadrantParser.new
|
|
25
|
+
# source = <<~MERMAID
|
|
26
|
+
# quadrantChart
|
|
27
|
+
# title Product Analysis
|
|
28
|
+
# x-axis Low Cost --> High Cost
|
|
29
|
+
# y-axis Low Value --> High Value
|
|
30
|
+
# Product A: [0.3, 0.7]
|
|
31
|
+
# MERMAID
|
|
32
|
+
# diagram = parser.parse(source)
|
|
33
|
+
class QuadrantParser < Base
|
|
34
|
+
# Parses quadrant chart diagram source into a QuadrantChart model.
|
|
35
|
+
#
|
|
36
|
+
# @param source [String] the Mermaid quadrant chart diagram source
|
|
37
|
+
# @return [Diagram::QuadrantChart] the parsed quadrant chart diagram
|
|
38
|
+
# @raise [ParseError] if syntax is invalid
|
|
39
|
+
def parse(source)
|
|
40
|
+
grammar = Grammars::Quadrant.new
|
|
41
|
+
|
|
42
|
+
begin
|
|
43
|
+
parse_tree = grammar.parse(source)
|
|
44
|
+
rescue Parslet::ParseFailed => e
|
|
45
|
+
raise ParseError, "Syntax error at #{e.parse_failure_cause.pos}: " \
|
|
46
|
+
"#{e.parse_failure_cause}"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Transform parse tree to diagram model
|
|
50
|
+
transform = Transforms::Quadrant.new
|
|
51
|
+
diagram = transform.apply(parse_tree)
|
|
52
|
+
|
|
53
|
+
diagram
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
require_relative "grammars/radar"
|
|
5
|
+
require_relative "transforms/radar"
|
|
6
|
+
require_relative "../diagram/radar"
|
|
7
|
+
|
|
8
|
+
module Sirena
|
|
9
|
+
module Parser
|
|
10
|
+
# Radar chart parser for Mermaid radar-beta diagram syntax.
|
|
11
|
+
#
|
|
12
|
+
# Uses Parslet grammar-based parsing to handle radar chart syntax
|
|
13
|
+
# with axes and multiple data curves.
|
|
14
|
+
#
|
|
15
|
+
# Parses radar charts with support for:
|
|
16
|
+
# - Title and accessibility metadata
|
|
17
|
+
# - Axis definitions with labels
|
|
18
|
+
# - Multiple data curves/datasets
|
|
19
|
+
# - Configuration options (ticks, legend, graticule, min/max)
|
|
20
|
+
#
|
|
21
|
+
# @example Parse a simple radar chart
|
|
22
|
+
# parser = RadarParser.new
|
|
23
|
+
# source = <<~MERMAID
|
|
24
|
+
# radar-beta
|
|
25
|
+
# title Skills Assessment
|
|
26
|
+
# axis A, B, C
|
|
27
|
+
# curve mycurve{1, 2, 3}
|
|
28
|
+
# MERMAID
|
|
29
|
+
# diagram = parser.parse(source)
|
|
30
|
+
class RadarParser < Base
|
|
31
|
+
# Parses radar diagram source into a RadarChart model.
|
|
32
|
+
#
|
|
33
|
+
# @param source [String] the Mermaid radar diagram source
|
|
34
|
+
# @return [Diagram::RadarChart] the parsed radar chart
|
|
35
|
+
# @raise [ParseError] if syntax is invalid
|
|
36
|
+
def parse(source)
|
|
37
|
+
grammar = Grammars::Radar.new
|
|
38
|
+
|
|
39
|
+
begin
|
|
40
|
+
parse_tree = grammar.parse(source)
|
|
41
|
+
rescue Parslet::ParseFailed => e
|
|
42
|
+
raise ParseError, "Syntax error at #{e.parse_failure_cause.pos}: " \
|
|
43
|
+
"#{e.parse_failure_cause}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Transform parse tree to intermediate representation
|
|
47
|
+
transform = Transforms::Radar.new
|
|
48
|
+
result = transform.apply(parse_tree)
|
|
49
|
+
|
|
50
|
+
# Create the diagram model
|
|
51
|
+
create_diagram(result)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def create_diagram(result)
|
|
57
|
+
diagram = Diagram::RadarChart.new
|
|
58
|
+
diagram.title = result[:title]
|
|
59
|
+
diagram.acc_title = result[:acc_title]
|
|
60
|
+
diagram.acc_descr = result[:acc_descr]
|
|
61
|
+
diagram.options = result[:options]
|
|
62
|
+
|
|
63
|
+
# Create axes
|
|
64
|
+
result[:axes].each do |axis_data|
|
|
65
|
+
axis = Diagram::RadarAxis.new(
|
|
66
|
+
axis_data[:id],
|
|
67
|
+
axis_data[:label]
|
|
68
|
+
)
|
|
69
|
+
diagram.axes << axis
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Create curves
|
|
73
|
+
result[:curves].each do |curve_data|
|
|
74
|
+
curve = Diagram::RadarCurve.new(
|
|
75
|
+
curve_data[:id],
|
|
76
|
+
curve_data[:label]
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Add values - handle both positional and named formats
|
|
80
|
+
values = Array(curve_data[:values])
|
|
81
|
+
|
|
82
|
+
if values.first.is_a?(Hash) && values.first[:axis]
|
|
83
|
+
# Named format: { axis: "A", value: 1.0 }
|
|
84
|
+
values.each do |val|
|
|
85
|
+
curve.add_value(val[:axis], val[:value])
|
|
86
|
+
end
|
|
87
|
+
else
|
|
88
|
+
# Positional format: [1.0, 2.0, 3.0]
|
|
89
|
+
values.each_with_index do |val, idx|
|
|
90
|
+
next unless diagram.axes[idx]
|
|
91
|
+
|
|
92
|
+
axis_id = diagram.axes[idx].id
|
|
93
|
+
curve.add_value(axis_id, val)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
diagram.curves << curve
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
diagram
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
require_relative 'grammars/requirement'
|
|
5
|
+
require_relative 'transforms/requirement'
|
|
6
|
+
require_relative '../diagram/requirement'
|
|
7
|
+
|
|
8
|
+
module Sirena
|
|
9
|
+
module Parser
|
|
10
|
+
# Requirement diagram parser for Mermaid requirement diagram syntax.
|
|
11
|
+
#
|
|
12
|
+
# Parses requirement diagrams with support for:
|
|
13
|
+
# - Requirements with properties (id, text, risk, verifymethod)
|
|
14
|
+
# - Multiple requirement types (requirement, functionalRequirement, etc.)
|
|
15
|
+
# - Elements with properties (type, docref)
|
|
16
|
+
# - Relationships (contains, copies, derives, satisfies, verifies, refines, traces)
|
|
17
|
+
# - Styling directives
|
|
18
|
+
# - Class definitions and assignments
|
|
19
|
+
#
|
|
20
|
+
# @example Parse a simple requirement diagram
|
|
21
|
+
# parser = RequirementParser.new
|
|
22
|
+
# diagram = parser.parse("requirementDiagram\n requirement test_req { id: 1 }")
|
|
23
|
+
class RequirementParser < Base
|
|
24
|
+
# Parses requirement diagram source into a RequirementDiagram model.
|
|
25
|
+
#
|
|
26
|
+
# @param source [String] the Mermaid requirement diagram source
|
|
27
|
+
# @return [Diagram::RequirementDiagram] the parsed requirement diagram
|
|
28
|
+
# @raise [ParseError] if syntax is invalid
|
|
29
|
+
def parse(source)
|
|
30
|
+
grammar = Grammars::Requirement.new
|
|
31
|
+
|
|
32
|
+
begin
|
|
33
|
+
tree = grammar.parse(source)
|
|
34
|
+
diagram = Transforms::Requirement.apply(tree)
|
|
35
|
+
diagram
|
|
36
|
+
rescue Parslet::ParseFailed => e
|
|
37
|
+
raise ParseError, format_parse_error(e, source)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
# Formats a Parslet parse error with context.
|
|
44
|
+
#
|
|
45
|
+
# @param error [Parslet::ParseFailed] the parse error
|
|
46
|
+
# @param source [String] the source that failed to parse
|
|
47
|
+
# @return [String] formatted error message
|
|
48
|
+
def format_parse_error(error, source)
|
|
49
|
+
lines = source.lines
|
|
50
|
+
line_num = error.parse_failure_cause.source.line_and_column[0]
|
|
51
|
+
col_num = error.parse_failure_cause.source.line_and_column[1]
|
|
52
|
+
|
|
53
|
+
context = []
|
|
54
|
+
context << "Parse error at line #{line_num}, column #{col_num}:"
|
|
55
|
+
|
|
56
|
+
# Show the problematic line
|
|
57
|
+
if line_num > 0 && line_num <= lines.length
|
|
58
|
+
context << lines[line_num - 1].chomp
|
|
59
|
+
context << (' ' * (col_num - 1)) + '^'
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context << error.parse_failure_cause.to_s
|
|
63
|
+
context.join("\n")
|
|
64
|
+
rescue StandardError
|
|
65
|
+
# Fallback to simple error message
|
|
66
|
+
"Parse error: #{error.message}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
require_relative "grammars/sankey"
|
|
5
|
+
require_relative "transforms/sankey"
|
|
6
|
+
require_relative "../diagram/sankey"
|
|
7
|
+
|
|
8
|
+
module Sirena
|
|
9
|
+
module Parser
|
|
10
|
+
# Sankey parser for Mermaid sankey diagram syntax.
|
|
11
|
+
#
|
|
12
|
+
# Uses Parslet grammar-based parsing to handle Sankey diagram syntax
|
|
13
|
+
# with flows between nodes and optional node labels.
|
|
14
|
+
#
|
|
15
|
+
# Parses Sankey diagrams with support for:
|
|
16
|
+
# - Flow definitions (CSV format: source,target,value)
|
|
17
|
+
# - Optional node declarations with labels
|
|
18
|
+
# - Automatic node discovery from flows
|
|
19
|
+
# - Numeric flow values (integer or float)
|
|
20
|
+
# - Comments
|
|
21
|
+
#
|
|
22
|
+
# @example Parse a simple Sankey diagram
|
|
23
|
+
# parser = SankeyParser.new
|
|
24
|
+
# diagram = parser.parse(<<~SANKEY)
|
|
25
|
+
# sankey-beta
|
|
26
|
+
# A,B,10
|
|
27
|
+
# B,C,20
|
|
28
|
+
# A,D,5
|
|
29
|
+
# SANKEY
|
|
30
|
+
#
|
|
31
|
+
# @example Parse Sankey with node labels
|
|
32
|
+
# parser = SankeyParser.new
|
|
33
|
+
# diagram = parser.parse(<<~SANKEY)
|
|
34
|
+
# sankey-beta
|
|
35
|
+
# Source [Energy Source]
|
|
36
|
+
# Process [Processing Plant]
|
|
37
|
+
# Source,Process,100
|
|
38
|
+
# Process,Output,70
|
|
39
|
+
# SANKEY
|
|
40
|
+
class SankeyParser < Base
|
|
41
|
+
# Parses Sankey diagram source into a SankeyDiagram model.
|
|
42
|
+
#
|
|
43
|
+
# @param source [String] the Mermaid Sankey diagram source
|
|
44
|
+
# @return [Diagram::SankeyDiagram] the parsed Sankey diagram
|
|
45
|
+
# @raise [ParseError] if syntax is invalid
|
|
46
|
+
def parse(source)
|
|
47
|
+
grammar = Grammars::Sankey.new
|
|
48
|
+
|
|
49
|
+
begin
|
|
50
|
+
parse_tree = grammar.parse(source)
|
|
51
|
+
rescue Parslet::ParseFailed => e
|
|
52
|
+
raise ParseError, "Syntax error at #{e.parse_failure_cause.pos}: " \
|
|
53
|
+
"#{e.parse_failure_cause}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Transform parse tree to diagram model
|
|
57
|
+
transform = Transforms::Sankey.new
|
|
58
|
+
diagram = transform.apply(parse_tree)
|
|
59
|
+
|
|
60
|
+
diagram
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
require_relative 'grammars/sequence'
|
|
5
|
+
require_relative 'transforms/sequence'
|
|
6
|
+
require_relative '../diagram/sequence'
|
|
7
|
+
|
|
8
|
+
module Sirena
|
|
9
|
+
module Parser
|
|
10
|
+
# Sequence parser for Mermaid sequence diagram syntax.
|
|
11
|
+
#
|
|
12
|
+
# Uses Parslet grammar-based parsing to correctly handle complex arrow
|
|
13
|
+
# patterns with activation modifiers (e.g., `->>+`, `-->>-`) that cannot
|
|
14
|
+
# be parsed accurately with regex-based lexers.
|
|
15
|
+
#
|
|
16
|
+
# Parses sequence diagrams with support for:
|
|
17
|
+
# - Participant declarations (participant, actor)
|
|
18
|
+
# - Multiple message types with activation modifiers
|
|
19
|
+
# - Activations and deactivations
|
|
20
|
+
# - Notes (left of, right of, over)
|
|
21
|
+
# - Control structures (loop, alt, opt, par, critical, break)
|
|
22
|
+
# - Box grouping
|
|
23
|
+
#
|
|
24
|
+
# @example Parse a simple sequence diagram
|
|
25
|
+
# parser = SequenceParser.new
|
|
26
|
+
# diagram = parser.parse("sequenceDiagram\nAlice->>Bob: Hello")
|
|
27
|
+
class SequenceParser < Base
|
|
28
|
+
# Parses sequence diagram source into a Sequence diagram model.
|
|
29
|
+
#
|
|
30
|
+
# @param source [String] the Mermaid sequence diagram source
|
|
31
|
+
# @return [Diagram::Sequence] the parsed sequence diagram
|
|
32
|
+
# @raise [ParseError] if syntax is invalid
|
|
33
|
+
def parse(source)
|
|
34
|
+
grammar = Grammars::Sequence.new
|
|
35
|
+
|
|
36
|
+
begin
|
|
37
|
+
parse_tree = grammar.parse(source)
|
|
38
|
+
rescue Parslet::ParseFailed => e
|
|
39
|
+
raise ParseError, "Syntax error at #{e.parse_failure_cause.pos}: " \
|
|
40
|
+
"#{e.parse_failure_cause}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Transform parse tree to diagram model
|
|
44
|
+
transform = Transforms::Sequence.new
|
|
45
|
+
diagram = transform.apply(parse_tree)
|
|
46
|
+
|
|
47
|
+
diagram
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
require_relative 'grammars/state_diagram'
|
|
5
|
+
require_relative 'transforms/state_diagram'
|
|
6
|
+
require_relative '../diagram/state_diagram'
|
|
7
|
+
|
|
8
|
+
module Sirena
|
|
9
|
+
module Parser
|
|
10
|
+
# State diagram parser for Mermaid state diagram syntax.
|
|
11
|
+
#
|
|
12
|
+
# Parses state diagrams with support for:
|
|
13
|
+
# - Normal states with labels
|
|
14
|
+
# - Special states (start [*], end [*], choice, fork, join)
|
|
15
|
+
# - Transitions with triggers and guard conditions
|
|
16
|
+
# - Composite/nested states
|
|
17
|
+
# - Direction specification (TD, LR, etc.)
|
|
18
|
+
#
|
|
19
|
+
# @example Parse a simple state diagram
|
|
20
|
+
# parser = StateDiagramParser.new
|
|
21
|
+
# diagram = parser.parse("stateDiagram-v2\n[*]-->Idle\nIdle-->Active")
|
|
22
|
+
class StateDiagramParser < Base
|
|
23
|
+
# Parses state diagram source into a StateDiagram model.
|
|
24
|
+
#
|
|
25
|
+
# @param source [String] the Mermaid state diagram source
|
|
26
|
+
# @return [Diagram::StateDiagram] the parsed state diagram
|
|
27
|
+
# @raise [ParseError] if syntax is invalid
|
|
28
|
+
def parse(source)
|
|
29
|
+
grammar = Grammars::StateDiagram.new
|
|
30
|
+
transform = Transforms::StateDiagram.new
|
|
31
|
+
|
|
32
|
+
begin
|
|
33
|
+
tree = grammar.parse(source)
|
|
34
|
+
transform.apply(tree)
|
|
35
|
+
rescue Parslet::ParseFailed => e
|
|
36
|
+
raise ParseError, format_parse_error(e, source)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def format_parse_error(error, source)
|
|
43
|
+
# Get the failure cause and position
|
|
44
|
+
cause = error.parse_failure_cause
|
|
45
|
+
pos = cause.pos
|
|
46
|
+
|
|
47
|
+
# Get line and column - handle boundary conditions
|
|
48
|
+
return "Parse error: #{error.message}" if pos.nil? || pos < 0 || pos > source.length
|
|
49
|
+
|
|
50
|
+
lines = source[0...pos].split("\n")
|
|
51
|
+
line_num = lines.size
|
|
52
|
+
col_num = lines.last&.size || 0
|
|
53
|
+
|
|
54
|
+
# Get the problematic line
|
|
55
|
+
all_lines = source.split("\n")
|
|
56
|
+
problem_line = all_lines[line_num - 1] || ''
|
|
57
|
+
|
|
58
|
+
# Build error message
|
|
59
|
+
msg = "Parse error at line #{line_num}, column #{col_num}\n"
|
|
60
|
+
msg += " #{problem_line}\n"
|
|
61
|
+
msg += " #{' ' * col_num}^\n" if col_num >= 0
|
|
62
|
+
msg += "Expected: #{cause.expected_string}"
|
|
63
|
+
msg
|
|
64
|
+
rescue StandardError => e
|
|
65
|
+
"Parse error: #{error.message} (#{e.message})"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|