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,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+ require_relative "grammars/timeline"
5
+ require_relative "transforms/timeline"
6
+ require_relative "../diagram/timeline"
7
+
8
+ module Sirena
9
+ module Parser
10
+ # Timeline parser for Mermaid timeline diagram syntax.
11
+ #
12
+ # Uses Parslet grammar-based parsing to handle timeline syntax
13
+ # with sections, events, and chronological ordering.
14
+ #
15
+ # Parses timelines with support for:
16
+ # - Title declarations
17
+ # - Section grouping (periods, categories)
18
+ # - Events with timestamps and descriptions
19
+ # - Multiple descriptions per timestamp
20
+ # - Tasks within sections
21
+ # - Accessibility features (accTitle, accDescr)
22
+ # - Comments
23
+ #
24
+ # @example Parse a simple timeline
25
+ # parser = TimelineParser.new
26
+ # diagram = parser.parse(<<~TIMELINE)
27
+ # timeline
28
+ # title History of Social Media
29
+ # 2002 : LinkedIn
30
+ # 2004 : Facebook : Google
31
+ # 2005 : YouTube
32
+ # TIMELINE
33
+ class TimelineParser < Base
34
+ # Parses timeline diagram source into a Timeline diagram model.
35
+ #
36
+ # @param source [String] the Mermaid timeline diagram source
37
+ # @return [Diagram::Timeline] the parsed timeline diagram
38
+ # @raise [ParseError] if syntax is invalid
39
+ def parse(source)
40
+ grammar = Grammars::Timeline.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::Timeline.new
51
+ diagram = transform.apply(parse_tree)
52
+
53
+ diagram
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../diagram/architecture'
4
+
5
+ module Sirena
6
+ module Parser
7
+ module Transforms
8
+ # Transform for architecture diagrams
9
+ class Architecture
10
+ def apply(tree)
11
+ diagram = Diagram::ArchitectureDiagram.new
12
+
13
+ # Process tree
14
+ if tree.is_a?(Array)
15
+ tree.each do |item|
16
+ process_statement(diagram, item) if item.is_a?(Hash)
17
+ end
18
+ elsif tree.is_a?(Hash)
19
+ process_statement(diagram, tree)
20
+ end
21
+
22
+ diagram
23
+ end
24
+
25
+ private
26
+
27
+ def process_statement(diagram, stmt)
28
+ return unless stmt.is_a?(Hash)
29
+
30
+ if stmt[:header]
31
+ # Skip header marker
32
+ nil
33
+ elsif stmt[:title]
34
+ diagram.title = extract_text(stmt[:title])
35
+ elsif stmt[:acc_title]
36
+ diagram.acc_title = extract_text(stmt[:acc_title])
37
+ elsif stmt[:acc_descr]
38
+ diagram.acc_descr = extract_text(stmt[:acc_descr])
39
+ elsif stmt[:from] && stmt[:to]
40
+ # Edge: has from and to
41
+ diagram.edges << create_edge(stmt)
42
+ elsif stmt[:stmt_type]
43
+ # Check statement type to distinguish group from service
44
+ if extract_text(stmt[:stmt_type]) == 'group'
45
+ diagram.groups << create_group(stmt)
46
+ elsif extract_text(stmt[:stmt_type]) == 'service'
47
+ diagram.services << create_service(stmt)
48
+ end
49
+ end
50
+ end
51
+
52
+ def create_group(data)
53
+ group = Diagram::ArchitectureDiagram::Group.new
54
+ group.id = extract_text(data[:id]) if data[:id]
55
+ group.label = extract_text(data[:label]) if data[:label]
56
+ group.icon = extract_text(data[:icon]) if data[:icon]
57
+ group.parent_id = extract_text(data[:parent]) if data[:parent] && !data[:parent].to_s.empty?
58
+ group
59
+ end
60
+
61
+ def create_service(data)
62
+ service = Diagram::ArchitectureDiagram::Service.new
63
+ service.id = extract_text(data[:id]) if data[:id]
64
+ service.label = extract_text(data[:label]) if data[:label]
65
+ service.icon = extract_text(data[:icon]) if data[:icon]
66
+ service.group_id = extract_text(data[:group]) if data[:group] && !data[:group].to_s.empty?
67
+ service
68
+ end
69
+
70
+ def create_edge(data)
71
+ edge = Diagram::ArchitectureDiagram::Edge.new
72
+ edge.from_id = extract_text(data[:from]) if data[:from]
73
+ edge.to_id = extract_text(data[:to]) if data[:to]
74
+ edge.from_position = extract_text(data[:from_pos]) if data[:from_pos]
75
+ edge.to_position = extract_text(data[:to_pos]) if data[:to_pos]
76
+ edge.label = extract_text(data[:label]) if data[:label]
77
+ edge
78
+ end
79
+
80
+ def extract_text(value)
81
+ case value
82
+ when Hash
83
+ if value[:string]
84
+ value[:string].to_s
85
+ else
86
+ value.values.first.to_s
87
+ end
88
+ when String
89
+ value
90
+ else
91
+ value.to_s
92
+ end.strip
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,254 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'parslet'
4
+ require_relative '../../diagram/block'
5
+
6
+ module Sirena
7
+ module Parser
8
+ module Transforms
9
+ # Transform for converting block diagram parse trees to diagram models.
10
+ #
11
+ # Handles transformation of blocks, compound blocks, connections, and
12
+ # styling directives from Parslet parse trees into BlockDiagram objects.
13
+ class Block < Parslet::Transform
14
+ # Shape delimiter to type mapping
15
+ SHAPE_MAP = {
16
+ '[]' => 'rect',
17
+ '(())' => 'circle'
18
+ }.freeze
19
+
20
+ # Arrow type mapping
21
+ ARROW_MAP = {
22
+ '-->' => 'arrow',
23
+ '---' => 'line'
24
+ }.freeze
25
+
26
+ # Block ID
27
+ rule(block_id: simple(:id)) { id.to_s }
28
+ rule(block_id: { string: simple(:s) }) { s.to_s }
29
+
30
+ # Block width (grammar includes colon, e.g. ":2")
31
+ rule(block_width: simple(:w)) { w.to_s.sub(/^:/, '').to_i }
32
+
33
+ # Arrow direction
34
+ rule(arrow_direction: { direction: simple(:d) }) { d.to_s }
35
+
36
+ # Shape with label
37
+ rule(
38
+ block_shape: {
39
+ open: simple(:o),
40
+ label: simple(:l),
41
+ close: simple(:c)
42
+ }
43
+ ) do
44
+ delims = "#{o}#{c}"
45
+ {
46
+ shape_type: SHAPE_MAP[delims] || 'rect',
47
+ label: l.to_s.strip
48
+ }
49
+ end
50
+
51
+ # Handle empty labels
52
+ rule(
53
+ block_shape: {
54
+ open: simple(:o),
55
+ label: sequence(:_),
56
+ close: simple(:c)
57
+ }
58
+ ) do
59
+ delims = "#{o}#{c}"
60
+ {
61
+ shape_type: SHAPE_MAP[delims] || 'rect',
62
+ label: ''
63
+ }
64
+ end
65
+
66
+ # Process parsed diagram
67
+ def self.apply(tree, diagram = nil)
68
+ diagram ||= Diagram::BlockDiagram.new
69
+
70
+ # Tree is an array of statement hashes
71
+ statements = tree.is_a?(Array) ? tree : [tree]
72
+
73
+ # Extract columns value first
74
+ statements.each do |stmt|
75
+ next unless stmt.is_a?(Hash)
76
+
77
+ if stmt[:columns_keyword] && stmt[:columns_value]
78
+ diagram.columns = stmt[:columns_value].to_s.to_i
79
+ break
80
+ end
81
+ end
82
+
83
+ # Process all statements
84
+ process_statements(diagram, statements)
85
+
86
+ diagram
87
+ end
88
+
89
+ def self.process_statements(diagram, statements, parent_block = nil)
90
+ statements.each do |stmt|
91
+ next unless stmt.is_a?(Hash)
92
+
93
+ if stmt[:columns_keyword]
94
+ # Already processed columns
95
+ next
96
+ elsif stmt[:space_keyword]
97
+ # Space placeholder
98
+ block = create_space_block
99
+ if parent_block
100
+ parent_block.add_child(block)
101
+ else
102
+ diagram.add_block(block)
103
+ end
104
+ elsif stmt[:arrow_id]
105
+ # Arrow block
106
+ block = create_arrow_block(stmt)
107
+ if parent_block
108
+ parent_block.add_child(block)
109
+ else
110
+ diagram.add_block(block)
111
+ end
112
+ elsif stmt[:compound_keyword]
113
+ # Compound block
114
+ block = create_compound_block(stmt)
115
+ if stmt[:compound_statements]
116
+ sub_stmts = stmt[:compound_statements]
117
+ sub_stmts = [sub_stmts] unless sub_stmts.is_a?(Array)
118
+ process_statements(diagram, sub_stmts, block)
119
+ end
120
+ if parent_block
121
+ parent_block.add_child(block)
122
+ else
123
+ diagram.add_block(block)
124
+ end
125
+ elsif stmt[:block_id]
126
+ # Regular block
127
+ block = create_block(stmt)
128
+ if parent_block
129
+ parent_block.add_child(block)
130
+ else
131
+ diagram.add_block(block)
132
+ end
133
+ elsif stmt[:from] && stmt[:to]
134
+ # Connection
135
+ connection = create_connection(stmt)
136
+ diagram.add_connection(connection)
137
+ elsif stmt[:style_keyword]
138
+ # Style directive
139
+ style = create_style(stmt)
140
+ diagram.add_style(style)
141
+ end
142
+ end
143
+ end
144
+
145
+ def self.create_space_block
146
+ Diagram::Block.new.tap do |b|
147
+ b.id = "space_#{rand(10000)}"
148
+ b.block_type = 'space'
149
+ end
150
+ end
151
+
152
+ def self.create_arrow_block(stmt)
153
+ Diagram::Block.new.tap do |b|
154
+ b.id = stmt[:arrow_id].to_s
155
+ b.block_type = 'arrow'
156
+ b.label = stmt[:arrow_label].to_s if stmt[:arrow_label]
157
+ b.direction = stmt[:arrow_direction].to_s if stmt[:arrow_direction]
158
+ end
159
+ end
160
+
161
+ def self.create_compound_block(stmt)
162
+ Diagram::Block.new.tap do |b|
163
+ # Generate ID if not provided
164
+ b.id = stmt[:compound_id] ? stmt[:compound_id].to_s : "compound_#{rand(10000)}"
165
+ b.is_compound = true
166
+ end
167
+ end
168
+
169
+ def self.create_block(stmt)
170
+ Diagram::Block.new.tap do |b|
171
+ b.id = stmt[:block_id].to_s
172
+
173
+ # Set width if specified
174
+ if stmt[:block_width]
175
+ width_val = stmt[:block_width]
176
+ # Handle both transformed integers and raw strings like ":2"
177
+ b.width = if width_val.is_a?(Hash)
178
+ width_val[:width].to_i
179
+ else
180
+ width_val.to_s.sub(/^:/, '').to_i
181
+ end
182
+ end
183
+
184
+ # Set shape and label if specified
185
+ if stmt[:block_shape]
186
+ shape_data = stmt[:block_shape]
187
+ if shape_data.is_a?(Hash)
188
+ # Check if it has open/close delimiters (raw parse tree)
189
+ if shape_data[:open] && shape_data[:close]
190
+ delims = "#{shape_data[:open]}#{shape_data[:close]}"
191
+ b.shape = SHAPE_MAP[delims] || 'rect'
192
+ label = shape_data[:label].to_s
193
+ # Strip surrounding quotes if present
194
+ label = label.gsub(/^["']|["']$/, '')
195
+ b.label = label
196
+ # Or if it's been transformed already
197
+ elsif shape_data[:shape_type]
198
+ b.shape = shape_data[:shape_type] || 'rect'
199
+ label = shape_data[:label] || b.id
200
+ # Strip surrounding quotes if present
201
+ label = label.to_s.gsub(/^["']|["']$/, '')
202
+ b.label = label
203
+ end
204
+ end
205
+ else
206
+ b.label = b.id
207
+ end
208
+ end
209
+ end
210
+
211
+ def self.create_connection(stmt)
212
+ Diagram::BlockConnection.new.tap do |c|
213
+ c.from = stmt[:from].to_s
214
+ c.to = stmt[:to].to_s
215
+
216
+ arrow_type = if stmt[:arrow]
217
+ stmt[:arrow][:arrow_type] || stmt[:arrow][:line_type]
218
+ end
219
+
220
+ if arrow_type
221
+ c.connection_type = ARROW_MAP[arrow_type.to_s] || 'arrow'
222
+ else
223
+ c.connection_type = 'arrow'
224
+ end
225
+ end
226
+ end
227
+
228
+ def self.create_style(stmt)
229
+ Diagram::BlockStyle.new.tap do |s|
230
+ s.block_id = stmt[:style_target].to_s
231
+
232
+ if stmt[:style_props]
233
+ props = stmt[:style_props]
234
+ props = [props] unless props.is_a?(Array)
235
+
236
+ props.each do |prop|
237
+ prop_str = prop.to_s.strip
238
+ if prop_str.start_with?('fill:')
239
+ s.fill = prop_str.sub('fill:', '').strip
240
+ elsif prop_str.start_with?('stroke:')
241
+ s.stroke = prop_str.sub('stroke:', '').strip
242
+ elsif prop_str.start_with?('stroke-width:')
243
+ s.stroke_width = prop_str.sub('stroke-width:', '').strip
244
+ else
245
+ s.properties << prop_str
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
251
+ end
252
+ end
253
+ end
254
+ end