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.
Files changed (382) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/build_deploy.yml +59 -0
  3. data/.github/workflows/links.yml +85 -0
  4. data/.github/workflows/rake.yml +15 -0
  5. data/.github/workflows/release.yml +27 -0
  6. data/.gitignore +68 -0
  7. data/.rspec +3 -0
  8. data/.rubocop.yml +14 -0
  9. data/.rubocop_todo.yml +70 -0
  10. data/ARCHITECTURE.md +744 -0
  11. data/Gemfile +12 -0
  12. data/LICENSE +25 -0
  13. data/README.adoc +357 -0
  14. data/Rakefile +11 -0
  15. data/docs/.gitignore +1 -0
  16. data/docs/Gemfile +13 -0
  17. data/docs/_config.yml +182 -0
  18. data/docs/_diagram_types/architecture-diagram.adoc +314 -0
  19. data/docs/_diagram_types/block-diagram.adoc +345 -0
  20. data/docs/_diagram_types/c4-diagram.adoc +559 -0
  21. data/docs/_diagram_types/class-diagram.adoc +816 -0
  22. data/docs/_diagram_types/er-diagram.adoc +719 -0
  23. data/docs/_diagram_types/error-diagram.adoc +114 -0
  24. data/docs/_diagram_types/examples/flowchart-examples.adoc +29 -0
  25. data/docs/_diagram_types/flowchart.adoc +488 -0
  26. data/docs/_diagram_types/gantt-chart.adoc +502 -0
  27. data/docs/_diagram_types/git-graph.adoc +600 -0
  28. data/docs/_diagram_types/index.adoc +192 -0
  29. data/docs/_diagram_types/info-diagram.adoc +103 -0
  30. data/docs/_diagram_types/kanban-diagram.adoc +262 -0
  31. data/docs/_diagram_types/mindmap.adoc +603 -0
  32. data/docs/_diagram_types/packet-diagram.adoc +378 -0
  33. data/docs/_diagram_types/pie-chart.adoc +335 -0
  34. data/docs/_diagram_types/quadrant-chart.adoc +406 -0
  35. data/docs/_diagram_types/radar-chart.adoc +528 -0
  36. data/docs/_diagram_types/requirement-diagram.adoc +416 -0
  37. data/docs/_diagram_types/sankey-diagram.adoc +357 -0
  38. data/docs/_diagram_types/sequence-diagram.adoc +664 -0
  39. data/docs/_diagram_types/state-diagram.adoc +658 -0
  40. data/docs/_diagram_types/timeline.adoc +352 -0
  41. data/docs/_diagram_types/treemap-diagram.adoc +462 -0
  42. data/docs/_diagram_types/user-journey.adoc +602 -0
  43. data/docs/_features/index.adoc +129 -0
  44. data/docs/_guides/cli-reference.adoc +203 -0
  45. data/docs/_guides/index.adoc +56 -0
  46. data/docs/_guides/installation.adoc +100 -0
  47. data/docs/_guides/quick-start.adoc +132 -0
  48. data/docs/_pages/comparison.adoc +441 -0
  49. data/docs/_pages/compatibility.adoc +300 -0
  50. data/docs/_pages/index.adoc +39 -0
  51. data/docs/_references/index.adoc +103 -0
  52. data/docs/_tutorials/index.adoc +57 -0
  53. data/docs/index.adoc +166 -0
  54. data/docs/lychee.toml +54 -0
  55. data/examples/.gitignore +10 -0
  56. data/examples/README.adoc +196 -0
  57. data/examples/README.md +64 -0
  58. data/examples/architecture/01-basic-services.mmd +9 -0
  59. data/examples/architecture/01-basic-services.svg +37 -0
  60. data/examples/architecture/02-service-groups.mmd +16 -0
  61. data/examples/architecture/02-service-groups.svg +55 -0
  62. data/examples/architecture/README.adoc +79 -0
  63. data/examples/block/01-basic-blocks.mmd +13 -0
  64. data/examples/block/01-basic-blocks.svg +44 -0
  65. data/examples/block/02-block-shapes.mmd +13 -0
  66. data/examples/block/02-block-shapes.svg +47 -0
  67. data/examples/block/README.adoc +85 -0
  68. data/examples/c4/01-context-diagram.mmd +10 -0
  69. data/examples/c4/01-context-diagram.svg +45 -0
  70. data/examples/c4/02-container-diagram.mmd +24 -0
  71. data/examples/c4/02-container-diagram.svg +105 -0
  72. data/examples/c4/README.adoc +92 -0
  73. data/examples/class_diagram/01-basic-classes.mmd +61 -0
  74. data/examples/class_diagram/01-basic-classes.svg +117 -0
  75. data/examples/class_diagram/02-relationships.mmd +61 -0
  76. data/examples/class_diagram/02-relationships.svg +129 -0
  77. data/examples/class_diagram/README.adoc +93 -0
  78. data/examples/er_diagram/01-basic-entities.mmd +64 -0
  79. data/examples/er_diagram/01-basic-entities.svg +5 -0
  80. data/examples/er_diagram/02-cardinality.mmd +57 -0
  81. data/examples/er_diagram/02-cardinality.svg +125 -0
  82. data/examples/er_diagram/README.adoc +88 -0
  83. data/examples/error/01-basic-error.mmd +1 -0
  84. data/examples/error/01-basic-error.svg +13 -0
  85. data/examples/error/02-error-display.mmd +1 -0
  86. data/examples/error/02-error-display.svg +13 -0
  87. data/examples/error/README.adoc +71 -0
  88. data/examples/error_message_example.svg +13 -0
  89. data/examples/flowchart/00-original.mmd +13 -0
  90. data/examples/flowchart/00-original.svg +5 -0
  91. data/examples/flowchart/01-basic-flow.mmd +7 -0
  92. data/examples/flowchart/01-basic-flow.svg +52 -0
  93. data/examples/flowchart/01-basic-flow.yml +13 -0
  94. data/examples/flowchart/02*.svg +87 -0
  95. data/examples/flowchart/02-node-shapes.mmd +9 -0
  96. data/examples/flowchart/02-node-shapes.svg +33 -0
  97. data/examples/flowchart/03-edge-types.mmd +7 -0
  98. data/examples/flowchart/03-edge-types.svg +53 -0
  99. data/examples/flowchart/04-subgraphs.mmd +9 -0
  100. data/examples/flowchart/04-subgraphs.svg +33 -0
  101. data/examples/flowchart/05-styling.mmd +9 -0
  102. data/examples/flowchart/05-styling.svg +33 -0
  103. data/examples/flowchart/06-complex-flow.mmd +8 -0
  104. data/examples/flowchart/06-complex-flow.svg +59 -0
  105. data/examples/flowchart/README.adoc +167 -0
  106. data/examples/gantt/01-simple-timeline.* +14 -0
  107. data/examples/gantt/01-simple-timeline.mmd +6 -0
  108. data/examples/gantt/01-simple-timeline.svg +26 -0
  109. data/examples/gantt/02-task-dependencies.mmd +6 -0
  110. data/examples/gantt/02-task-dependencies.svg +26 -0
  111. data/examples/gantt/README.adoc +86 -0
  112. data/examples/git_graph/01-linear-history.mmd +12 -0
  113. data/examples/git_graph/01-linear-history.svg +26 -0
  114. data/examples/git_graph/02-branching.mmd +12 -0
  115. data/examples/git_graph/02-branching.svg +26 -0
  116. data/examples/git_graph/README.adoc +73 -0
  117. data/examples/info/02-showinfo.mmd +1 -0
  118. data/examples/info/02-showinfo.svg +10 -0
  119. data/examples/info/README.adoc +58 -0
  120. data/examples/info_showinfo_example.svg +10 -0
  121. data/examples/kanban/01-simple-board.mmd +8 -0
  122. data/examples/kanban/01-simple-board.svg +43 -0
  123. data/examples/kanban/02-workflow.mmd +8 -0
  124. data/examples/kanban/02-workflow.svg +43 -0
  125. data/examples/kanban/README.adoc +79 -0
  126. data/examples/mindmap/01-simple-tree.mmd +19 -0
  127. data/examples/mindmap/01-simple-tree.svg +61 -0
  128. data/examples/mindmap/02-knowledge-map.mmd +19 -0
  129. data/examples/mindmap/02-knowledge-map.svg +61 -0
  130. data/examples/mindmap/README.adoc +77 -0
  131. data/examples/packet/01-basic-packet.* +17 -0
  132. data/examples/packet/01-basic-packet.mmd +4 -0
  133. data/examples/packet/01-basic-packet.svg +82 -0
  134. data/examples/packet/README.adoc +58 -0
  135. data/examples/pie/01-simple-chart.mmd +5 -0
  136. data/examples/pie/01-simple-chart.svg +17 -0
  137. data/examples/pie/02-labeled-slices.mmd +6 -0
  138. data/examples/pie/02-labeled-slices.svg +19 -0
  139. data/examples/pie/README.adoc +75 -0
  140. data/examples/quadrant/01-basic-quadrant.mmd +13 -0
  141. data/examples/quadrant/01-basic-quadrant.svg +33 -0
  142. data/examples/quadrant/02-positioned-items.mmd +14 -0
  143. data/examples/quadrant/02-positioned-items.svg +35 -0
  144. data/examples/quadrant/README.adoc +84 -0
  145. data/examples/radar/01-simple-radar.* +5 -0
  146. data/examples/radar/01-simple-radar.mmd +3 -0
  147. data/examples/radar/01-simple-radar.svg +25 -0
  148. data/examples/radar/02-multiple-curves.mmd +4 -0
  149. data/examples/radar/02-multiple-curves.svg +43 -0
  150. data/examples/radar/README.adoc +75 -0
  151. data/examples/requirement/01-basic-requirements.mmd +23 -0
  152. data/examples/requirement/01-basic-requirements.svg +49 -0
  153. data/examples/requirement/02-risk-levels.mmd +23 -0
  154. data/examples/requirement/02-risk-levels.svg +49 -0
  155. data/examples/requirement/README.adoc +85 -0
  156. data/examples/sankey/01-simple-flow.mmd +7 -0
  157. data/examples/sankey/01-simple-flow.svg +34 -0
  158. data/examples/sankey/02-multi-stage.mmd +11 -0
  159. data/examples/sankey/02-multi-stage.svg +44 -0
  160. data/examples/sankey/README.adoc +74 -0
  161. data/examples/sequence/01-basic-sequence.mmd +27 -0
  162. data/examples/sequence/01-basic-sequence.svg +5 -0
  163. data/examples/sequence/02-activations.mmd +17 -0
  164. data/examples/sequence/02-activations.svg +78 -0
  165. data/examples/sequence/README.adoc +86 -0
  166. data/examples/state_diagram/01-simple-states.mmd +29 -0
  167. data/examples/state_diagram/01-simple-states.svg +5 -0
  168. data/examples/state_diagram/02-composite.mmd +19 -0
  169. data/examples/state_diagram/02-composite.svg +81 -0
  170. data/examples/state_diagram/README.adoc +90 -0
  171. data/examples/timeline/01-simple-timeline.mmd +11 -0
  172. data/examples/timeline/01-simple-timeline.svg +36 -0
  173. data/examples/timeline/02-periods.mmd +15 -0
  174. data/examples/timeline/02-periods.svg +47 -0
  175. data/examples/timeline/README.adoc +78 -0
  176. data/examples/treemap/01-basic-treemap.mmd +12 -0
  177. data/examples/treemap/01-basic-treemap.svg +59 -0
  178. data/examples/treemap/README.adoc +59 -0
  179. data/examples/user_journey/01-simple-journey.mmd +23 -0
  180. data/examples/user_journey/01-simple-journey.svg +5 -0
  181. data/examples/user_journey/02-multi-actor.mmd +18 -0
  182. data/examples/user_journey/02-multi-actor.svg +129 -0
  183. data/examples/user_journey/README.adoc +81 -0
  184. data/examples/xychart/01-line-chart.mmd +5 -0
  185. data/examples/xychart/01-line-chart.svg +43 -0
  186. data/examples/xychart/02-bar-chart.mmd +7 -0
  187. data/examples/xychart/02-bar-chart.svg +48 -0
  188. data/examples/xychart/README.adoc +80 -0
  189. data/exe/sirena +7 -0
  190. data/lib/sirena/cli.rb +138 -0
  191. data/lib/sirena/commands/batch.rb +117 -0
  192. data/lib/sirena/commands/render.rb +80 -0
  193. data/lib/sirena/commands/types.rb +29 -0
  194. data/lib/sirena/commands/version.rb +24 -0
  195. data/lib/sirena/diagram/architecture.rb +46 -0
  196. data/lib/sirena/diagram/base.rb +61 -0
  197. data/lib/sirena/diagram/block.rb +81 -0
  198. data/lib/sirena/diagram/c4.rb +328 -0
  199. data/lib/sirena/diagram/class_diagram.rb +385 -0
  200. data/lib/sirena/diagram/er_diagram.rb +238 -0
  201. data/lib/sirena/diagram/error.rb +38 -0
  202. data/lib/sirena/diagram/flowchart.rb +160 -0
  203. data/lib/sirena/diagram/gantt.rb +71 -0
  204. data/lib/sirena/diagram/git_graph.rb +36 -0
  205. data/lib/sirena/diagram/info.rb +38 -0
  206. data/lib/sirena/diagram/kanban.rb +178 -0
  207. data/lib/sirena/diagram/mindmap.rb +54 -0
  208. data/lib/sirena/diagram/packet.rb +79 -0
  209. data/lib/sirena/diagram/pie.rb +115 -0
  210. data/lib/sirena/diagram/quadrant.rb +138 -0
  211. data/lib/sirena/diagram/radar.rb +52 -0
  212. data/lib/sirena/diagram/requirement.rb +133 -0
  213. data/lib/sirena/diagram/sankey.rb +217 -0
  214. data/lib/sirena/diagram/sequence.rb +242 -0
  215. data/lib/sirena/diagram/state_diagram.rb +237 -0
  216. data/lib/sirena/diagram/timeline.rb +171 -0
  217. data/lib/sirena/diagram/treemap.rb +84 -0
  218. data/lib/sirena/diagram/user_journey.rb +149 -0
  219. data/lib/sirena/diagram/xy_chart.rb +76 -0
  220. data/lib/sirena/diagram.rb +8 -0
  221. data/lib/sirena/diagram_registry.rb +101 -0
  222. data/lib/sirena/engine.rb +292 -0
  223. data/lib/sirena/parser/architecture.rb +41 -0
  224. data/lib/sirena/parser/base.rb +41 -0
  225. data/lib/sirena/parser/block.rb +72 -0
  226. data/lib/sirena/parser/c4.rb +53 -0
  227. data/lib/sirena/parser/class_diagram.rb +63 -0
  228. data/lib/sirena/parser/er_diagram.rb +40 -0
  229. data/lib/sirena/parser/error.rb +49 -0
  230. data/lib/sirena/parser/flowchart.rb +71 -0
  231. data/lib/sirena/parser/gantt.rb +60 -0
  232. data/lib/sirena/parser/git_graph.rb +95 -0
  233. data/lib/sirena/parser/grammars/architecture.rb +145 -0
  234. data/lib/sirena/parser/grammars/block.rb +190 -0
  235. data/lib/sirena/parser/grammars/c4.rb +226 -0
  236. data/lib/sirena/parser/grammars/class_diagram.rb +284 -0
  237. data/lib/sirena/parser/grammars/common.rb +84 -0
  238. data/lib/sirena/parser/grammars/er_diagram.rb +114 -0
  239. data/lib/sirena/parser/grammars/error.rb +40 -0
  240. data/lib/sirena/parser/grammars/flowchart.rb +298 -0
  241. data/lib/sirena/parser/grammars/gantt.rb +252 -0
  242. data/lib/sirena/parser/grammars/git_graph.rb +167 -0
  243. data/lib/sirena/parser/grammars/info.rb +58 -0
  244. data/lib/sirena/parser/grammars/kanban.rb +83 -0
  245. data/lib/sirena/parser/grammars/mindmap.rb +115 -0
  246. data/lib/sirena/parser/grammars/packet.rb +73 -0
  247. data/lib/sirena/parser/grammars/pie.rb +128 -0
  248. data/lib/sirena/parser/grammars/quadrant.rb +199 -0
  249. data/lib/sirena/parser/grammars/radar.rb +150 -0
  250. data/lib/sirena/parser/grammars/requirement.rb +188 -0
  251. data/lib/sirena/parser/grammars/sankey.rb +104 -0
  252. data/lib/sirena/parser/grammars/sequence.rb +247 -0
  253. data/lib/sirena/parser/grammars/state_diagram.rb +172 -0
  254. data/lib/sirena/parser/grammars/timeline.rb +142 -0
  255. data/lib/sirena/parser/grammars/treemap.rb +120 -0
  256. data/lib/sirena/parser/grammars/xy_chart.rb +120 -0
  257. data/lib/sirena/parser/info.rb +49 -0
  258. data/lib/sirena/parser/kanban.rb +97 -0
  259. data/lib/sirena/parser/mindmap.rb +106 -0
  260. data/lib/sirena/parser/packet.rb +76 -0
  261. data/lib/sirena/parser/pie.rb +49 -0
  262. data/lib/sirena/parser/quadrant.rb +57 -0
  263. data/lib/sirena/parser/radar.rb +104 -0
  264. data/lib/sirena/parser/requirement.rb +70 -0
  265. data/lib/sirena/parser/sankey.rb +64 -0
  266. data/lib/sirena/parser/sequence.rb +51 -0
  267. data/lib/sirena/parser/state_diagram.rb +69 -0
  268. data/lib/sirena/parser/timeline.rb +57 -0
  269. data/lib/sirena/parser/transforms/architecture.rb +97 -0
  270. data/lib/sirena/parser/transforms/block.rb +254 -0
  271. data/lib/sirena/parser/transforms/c4.rb +347 -0
  272. data/lib/sirena/parser/transforms/class_diagram.rb +352 -0
  273. data/lib/sirena/parser/transforms/er_diagram.rb +169 -0
  274. data/lib/sirena/parser/transforms/error.rb +58 -0
  275. data/lib/sirena/parser/transforms/flowchart.rb +293 -0
  276. data/lib/sirena/parser/transforms/gantt.rb +215 -0
  277. data/lib/sirena/parser/transforms/git_graph.rb +160 -0
  278. data/lib/sirena/parser/transforms/info.rb +58 -0
  279. data/lib/sirena/parser/transforms/kanban.rb +176 -0
  280. data/lib/sirena/parser/transforms/mindmap.rb +227 -0
  281. data/lib/sirena/parser/transforms/packet.rb +63 -0
  282. data/lib/sirena/parser/transforms/pie.rb +143 -0
  283. data/lib/sirena/parser/transforms/quadrant.rb +177 -0
  284. data/lib/sirena/parser/transforms/radar.rb +126 -0
  285. data/lib/sirena/parser/transforms/requirement.rb +272 -0
  286. data/lib/sirena/parser/transforms/sankey.rb +122 -0
  287. data/lib/sirena/parser/transforms/sequence.rb +342 -0
  288. data/lib/sirena/parser/transforms/state_diagram.rb +292 -0
  289. data/lib/sirena/parser/transforms/timeline.rb +177 -0
  290. data/lib/sirena/parser/transforms/treemap.rb +81 -0
  291. data/lib/sirena/parser/transforms/xy_chart.rb +132 -0
  292. data/lib/sirena/parser/treemap.rb +98 -0
  293. data/lib/sirena/parser/user_journey.rb +120 -0
  294. data/lib/sirena/parser/xy_chart.rb +114 -0
  295. data/lib/sirena/parser.rb +8 -0
  296. data/lib/sirena/renderer/architecture.rb +251 -0
  297. data/lib/sirena/renderer/base.rb +251 -0
  298. data/lib/sirena/renderer/block.rb +286 -0
  299. data/lib/sirena/renderer/c4.rb +490 -0
  300. data/lib/sirena/renderer/class_diagram.rb +499 -0
  301. data/lib/sirena/renderer/er_diagram.rb +417 -0
  302. data/lib/sirena/renderer/error.rb +131 -0
  303. data/lib/sirena/renderer/flowchart.rb +301 -0
  304. data/lib/sirena/renderer/gantt.rb +331 -0
  305. data/lib/sirena/renderer/git_graph.rb +368 -0
  306. data/lib/sirena/renderer/info.rb +93 -0
  307. data/lib/sirena/renderer/kanban.rb +295 -0
  308. data/lib/sirena/renderer/mindmap.rb +396 -0
  309. data/lib/sirena/renderer/packet.rb +239 -0
  310. data/lib/sirena/renderer/pie.rb +235 -0
  311. data/lib/sirena/renderer/quadrant.rb +292 -0
  312. data/lib/sirena/renderer/radar.rb +323 -0
  313. data/lib/sirena/renderer/requirement.rb +371 -0
  314. data/lib/sirena/renderer/sankey.rb +255 -0
  315. data/lib/sirena/renderer/sequence.rb +424 -0
  316. data/lib/sirena/renderer/state_diagram.rb +328 -0
  317. data/lib/sirena/renderer/timeline.rb +304 -0
  318. data/lib/sirena/renderer/treemap.rb +152 -0
  319. data/lib/sirena/renderer/user_journey.rb +331 -0
  320. data/lib/sirena/renderer/xy_chart.rb +452 -0
  321. data/lib/sirena/renderer.rb +8 -0
  322. data/lib/sirena/svg/circle.rb +41 -0
  323. data/lib/sirena/svg/document.rb +103 -0
  324. data/lib/sirena/svg/element.rb +65 -0
  325. data/lib/sirena/svg/ellipse.rb +33 -0
  326. data/lib/sirena/svg/group.rb +71 -0
  327. data/lib/sirena/svg/line.rb +49 -0
  328. data/lib/sirena/svg/path.rb +76 -0
  329. data/lib/sirena/svg/polygon.rb +43 -0
  330. data/lib/sirena/svg/polyline.rb +35 -0
  331. data/lib/sirena/svg/rect.rb +57 -0
  332. data/lib/sirena/svg/style.rb +44 -0
  333. data/lib/sirena/svg/text.rb +72 -0
  334. data/lib/sirena/svg.rb +19 -0
  335. data/lib/sirena/text_measurement.rb +71 -0
  336. data/lib/sirena/theme/builtin/dark.yml +70 -0
  337. data/lib/sirena/theme/builtin/default.yml +80 -0
  338. data/lib/sirena/theme/builtin/high_contrast.yml +70 -0
  339. data/lib/sirena/theme/builtin/light.yml +70 -0
  340. data/lib/sirena/theme/color_palette.rb +48 -0
  341. data/lib/sirena/theme/effect_styles.rb +28 -0
  342. data/lib/sirena/theme/registry.rb +41 -0
  343. data/lib/sirena/theme/shape_styles.rb +28 -0
  344. data/lib/sirena/theme/spacing_config.rb +24 -0
  345. data/lib/sirena/theme/typography.rb +30 -0
  346. data/lib/sirena/theme.rb +69 -0
  347. data/lib/sirena/transform/architecture.rb +273 -0
  348. data/lib/sirena/transform/base.rb +199 -0
  349. data/lib/sirena/transform/block.rb +215 -0
  350. data/lib/sirena/transform/c4.rb +288 -0
  351. data/lib/sirena/transform/class_diagram.rb +296 -0
  352. data/lib/sirena/transform/er_diagram.rb +204 -0
  353. data/lib/sirena/transform/error.rb +39 -0
  354. data/lib/sirena/transform/flowchart.rb +161 -0
  355. data/lib/sirena/transform/gantt.rb +253 -0
  356. data/lib/sirena/transform/git_graph.rb +283 -0
  357. data/lib/sirena/transform/info.rb +39 -0
  358. data/lib/sirena/transform/kanban.rb +180 -0
  359. data/lib/sirena/transform/mindmap.rb +251 -0
  360. data/lib/sirena/transform/packet.rb +185 -0
  361. data/lib/sirena/transform/pie.rb +62 -0
  362. data/lib/sirena/transform/quadrant.rb +167 -0
  363. data/lib/sirena/transform/radar.rb +227 -0
  364. data/lib/sirena/transform/requirement.rb +233 -0
  365. data/lib/sirena/transform/sankey.rb +212 -0
  366. data/lib/sirena/transform/sequence.rb +143 -0
  367. data/lib/sirena/transform/state_diagram.rb +228 -0
  368. data/lib/sirena/transform/timeline.rb +139 -0
  369. data/lib/sirena/transform/treemap.rb +120 -0
  370. data/lib/sirena/transform/user_journey.rb +207 -0
  371. data/lib/sirena/transform/xy_chart.rb +273 -0
  372. data/lib/sirena/transform.rb +8 -0
  373. data/lib/sirena/version.rb +5 -0
  374. data/lib/sirena.rb +328 -0
  375. data/lib/tasks/benchmark.rake +532 -0
  376. data/lib/tasks/examples.rake +468 -0
  377. data/lib/tasks/generate_mermaid_fixtures.rake +363 -0
  378. data/lib/tasks/mermaid_fixtures.rake +46 -0
  379. data/scripts/extract_mermaid_tests.rb +493 -0
  380. data/scripts/rename_to_sirena.rb +73 -0
  381. data/sirena.gemspec +47 -0
  382. 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