@graphty/graph-io 0.0.0 → 0.2.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 (339) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +250 -28
  3. package/dist/chunks/children-CL3Cy0ez.js +238 -0
  4. package/dist/chunks/children-CL3Cy0ez.js.map +1 -0
  5. package/dist/chunks/escape-DyI8JofU.js +938 -0
  6. package/dist/chunks/escape-DyI8JofU.js.map +1 -0
  7. package/dist/chunks/importer-CQnJuWJw.js +2987 -0
  8. package/dist/chunks/importer-CQnJuWJw.js.map +1 -0
  9. package/dist/chunks/importer-CpCpfbxr.js +2015 -0
  10. package/dist/chunks/importer-CpCpfbxr.js.map +1 -0
  11. package/dist/chunks/importer-DbnGYr3_.js +2342 -0
  12. package/dist/chunks/importer-DbnGYr3_.js.map +1 -0
  13. package/dist/chunks/importer-GozH8DkN.js +3050 -0
  14. package/dist/chunks/importer-GozH8DkN.js.map +1 -0
  15. package/dist/chunks/records-CGpxszm1.js +605 -0
  16. package/dist/chunks/records-CGpxszm1.js.map +1 -0
  17. package/dist/chunks/text-CajMdVFy.js +189 -0
  18. package/dist/chunks/text-CajMdVFy.js.map +1 -0
  19. package/dist/chunks/writer-DxSKC7TL.js +2842 -0
  20. package/dist/chunks/writer-DxSKC7TL.js.map +1 -0
  21. package/dist/csv.d.ts +1 -0
  22. package/dist/csv.js +1702 -0
  23. package/dist/csv.js.map +1 -0
  24. package/dist/dot.d.ts +1 -0
  25. package/dist/dot.js +8 -0
  26. package/dist/dot.js.map +1 -0
  27. package/dist/gexf.d.ts +1 -0
  28. package/dist/gexf.js +3466 -0
  29. package/dist/gexf.js.map +1 -0
  30. package/dist/gml.d.ts +1 -0
  31. package/dist/gml.js +2647 -0
  32. package/dist/gml.js.map +1 -0
  33. package/dist/graph-io.d.ts +1 -0
  34. package/dist/graph-io.js +790 -0
  35. package/dist/graph-io.js.map +1 -0
  36. package/dist/graphml.d.ts +1 -0
  37. package/dist/graphml.js +8 -0
  38. package/dist/graphml.js.map +1 -0
  39. package/dist/json.d.ts +1 -0
  40. package/dist/json.js +11 -0
  41. package/dist/json.js.map +1 -0
  42. package/dist/neo4j.d.ts +1 -0
  43. package/dist/neo4j.js +2046 -0
  44. package/dist/neo4j.js.map +1 -0
  45. package/dist/pajek.d.ts +1 -0
  46. package/dist/pajek.js +8 -0
  47. package/dist/pajek.js.map +1 -0
  48. package/dist/src/children.d.ts +134 -0
  49. package/dist/src/children.d.ts.map +1 -0
  50. package/dist/src/children.js +274 -0
  51. package/dist/src/children.js.map +1 -0
  52. package/dist/src/common/attributes.d.ts +229 -0
  53. package/dist/src/common/attributes.d.ts.map +1 -0
  54. package/dist/src/common/attributes.js +368 -0
  55. package/dist/src/common/attributes.js.map +1 -0
  56. package/dist/src/common/codes.d.ts +105 -0
  57. package/dist/src/common/codes.d.ts.map +1 -0
  58. package/dist/src/common/codes.js +107 -0
  59. package/dist/src/common/codes.js.map +1 -0
  60. package/dist/src/common/declared-types.d.ts +84 -0
  61. package/dist/src/common/declared-types.d.ts.map +1 -0
  62. package/dist/src/common/declared-types.js +326 -0
  63. package/dist/src/common/declared-types.js.map +1 -0
  64. package/dist/src/common/direction.d.ts +206 -0
  65. package/dist/src/common/direction.d.ts.map +1 -0
  66. package/dist/src/common/direction.js +370 -0
  67. package/dist/src/common/direction.js.map +1 -0
  68. package/dist/src/common/escape.d.ts +92 -0
  69. package/dist/src/common/escape.d.ts.map +1 -0
  70. package/dist/src/common/escape.js +212 -0
  71. package/dist/src/common/escape.js.map +1 -0
  72. package/dist/src/common/export.d.ts +249 -0
  73. package/dist/src/common/export.d.ts.map +1 -0
  74. package/dist/src/common/export.js +594 -0
  75. package/dist/src/common/export.js.map +1 -0
  76. package/dist/src/common/format.d.ts +59 -0
  77. package/dist/src/common/format.d.ts.map +1 -0
  78. package/dist/src/common/format.js +106 -0
  79. package/dist/src/common/format.js.map +1 -0
  80. package/dist/src/common/ids.d.ts +83 -0
  81. package/dist/src/common/ids.d.ts.map +1 -0
  82. package/dist/src/common/ids.js +158 -0
  83. package/dist/src/common/ids.js.map +1 -0
  84. package/dist/src/common/input.d.ts +100 -0
  85. package/dist/src/common/input.d.ts.map +1 -0
  86. package/dist/src/common/input.js +335 -0
  87. package/dist/src/common/input.js.map +1 -0
  88. package/dist/src/common/lists.d.ts +34 -0
  89. package/dist/src/common/lists.d.ts.map +1 -0
  90. package/dist/src/common/lists.js +185 -0
  91. package/dist/src/common/lists.js.map +1 -0
  92. package/dist/src/common/options.d.ts +108 -0
  93. package/dist/src/common/options.d.ts.map +1 -0
  94. package/dist/src/common/options.js +265 -0
  95. package/dist/src/common/options.js.map +1 -0
  96. package/dist/src/common/report.d.ts +187 -0
  97. package/dist/src/common/report.d.ts.map +1 -0
  98. package/dist/src/common/report.js +274 -0
  99. package/dist/src/common/report.js.map +1 -0
  100. package/dist/src/common/temporal.d.ts +71 -0
  101. package/dist/src/common/temporal.d.ts.map +1 -0
  102. package/dist/src/common/temporal.js +266 -0
  103. package/dist/src/common/temporal.js.map +1 -0
  104. package/dist/src/common/text.d.ts +104 -0
  105. package/dist/src/common/text.d.ts.map +1 -0
  106. package/dist/src/common/text.js +255 -0
  107. package/dist/src/common/text.js.map +1 -0
  108. package/dist/src/common/weights.d.ts +77 -0
  109. package/dist/src/common/weights.d.ts.map +1 -0
  110. package/dist/src/common/weights.js +156 -0
  111. package/dist/src/common/weights.js.map +1 -0
  112. package/dist/src/common/writer.d.ts +51 -0
  113. package/dist/src/common/writer.d.ts.map +1 -0
  114. package/dist/src/common/writer.js +108 -0
  115. package/dist/src/common/writer.js.map +1 -0
  116. package/dist/src/common/xml.d.ts +245 -0
  117. package/dist/src/common/xml.d.ts.map +1 -0
  118. package/dist/src/common/xml.js +942 -0
  119. package/dist/src/common/xml.js.map +1 -0
  120. package/dist/src/formats/csv/exporter.d.ts +70 -0
  121. package/dist/src/formats/csv/exporter.d.ts.map +1 -0
  122. package/dist/src/formats/csv/exporter.js +682 -0
  123. package/dist/src/formats/csv/exporter.js.map +1 -0
  124. package/dist/src/formats/csv/header.d.ts +66 -0
  125. package/dist/src/formats/csv/header.d.ts.map +1 -0
  126. package/dist/src/formats/csv/header.js +152 -0
  127. package/dist/src/formats/csv/header.js.map +1 -0
  128. package/dist/src/formats/csv/importer.d.ts +82 -0
  129. package/dist/src/formats/csv/importer.d.ts.map +1 -0
  130. package/dist/src/formats/csv/importer.js +849 -0
  131. package/dist/src/formats/csv/importer.js.map +1 -0
  132. package/dist/src/formats/csv/index.d.ts +60 -0
  133. package/dist/src/formats/csv/index.d.ts.map +1 -0
  134. package/dist/src/formats/csv/index.js +63 -0
  135. package/dist/src/formats/csv/index.js.map +1 -0
  136. package/dist/src/formats/csv/records.d.ts +188 -0
  137. package/dist/src/formats/csv/records.d.ts.map +1 -0
  138. package/dist/src/formats/csv/records.js +702 -0
  139. package/dist/src/formats/csv/records.js.map +1 -0
  140. package/dist/src/formats/csv/values.d.ts +105 -0
  141. package/dist/src/formats/csv/values.d.ts.map +1 -0
  142. package/dist/src/formats/csv/values.js +192 -0
  143. package/dist/src/formats/csv/values.js.map +1 -0
  144. package/dist/src/formats/dot/exporter.d.ts +52 -0
  145. package/dist/src/formats/dot/exporter.d.ts.map +1 -0
  146. package/dist/src/formats/dot/exporter.js +836 -0
  147. package/dist/src/formats/dot/exporter.js.map +1 -0
  148. package/dist/src/formats/dot/importer.d.ts +102 -0
  149. package/dist/src/formats/dot/importer.d.ts.map +1 -0
  150. package/dist/src/formats/dot/importer.js +1291 -0
  151. package/dist/src/formats/dot/importer.js.map +1 -0
  152. package/dist/src/formats/dot/index.d.ts +7 -0
  153. package/dist/src/formats/dot/index.d.ts.map +1 -0
  154. package/dist/src/formats/dot/index.js +7 -0
  155. package/dist/src/formats/dot/index.js.map +1 -0
  156. package/dist/src/formats/dot/names.d.ts +29 -0
  157. package/dist/src/formats/dot/names.d.ts.map +1 -0
  158. package/dist/src/formats/dot/names.js +28 -0
  159. package/dist/src/formats/dot/names.js.map +1 -0
  160. package/dist/src/formats/dot/tokenizer.d.ts +114 -0
  161. package/dist/src/formats/dot/tokenizer.d.ts.map +1 -0
  162. package/dist/src/formats/dot/tokenizer.js +341 -0
  163. package/dist/src/formats/dot/tokenizer.js.map +1 -0
  164. package/dist/src/formats/gexf/exporter.d.ts +56 -0
  165. package/dist/src/formats/gexf/exporter.d.ts.map +1 -0
  166. package/dist/src/formats/gexf/exporter.js +1395 -0
  167. package/dist/src/formats/gexf/exporter.js.map +1 -0
  168. package/dist/src/formats/gexf/importer.d.ts +73 -0
  169. package/dist/src/formats/gexf/importer.d.ts.map +1 -0
  170. package/dist/src/formats/gexf/importer.js +1880 -0
  171. package/dist/src/formats/gexf/importer.js.map +1 -0
  172. package/dist/src/formats/gexf/index.d.ts +96 -0
  173. package/dist/src/formats/gexf/index.d.ts.map +1 -0
  174. package/dist/src/formats/gexf/index.js +97 -0
  175. package/dist/src/formats/gexf/index.js.map +1 -0
  176. package/dist/src/formats/gexf/schema.d.ts +135 -0
  177. package/dist/src/formats/gexf/schema.d.ts.map +1 -0
  178. package/dist/src/formats/gexf/schema.js +323 -0
  179. package/dist/src/formats/gexf/schema.js.map +1 -0
  180. package/dist/src/formats/gml/exporter.d.ts +69 -0
  181. package/dist/src/formats/gml/exporter.d.ts.map +1 -0
  182. package/dist/src/formats/gml/exporter.js +1093 -0
  183. package/dist/src/formats/gml/exporter.js.map +1 -0
  184. package/dist/src/formats/gml/importer.d.ts +66 -0
  185. package/dist/src/formats/gml/importer.d.ts.map +1 -0
  186. package/dist/src/formats/gml/importer.js +1331 -0
  187. package/dist/src/formats/gml/importer.js.map +1 -0
  188. package/dist/src/formats/gml/index.d.ts +85 -0
  189. package/dist/src/formats/gml/index.d.ts.map +1 -0
  190. package/dist/src/formats/gml/index.js +88 -0
  191. package/dist/src/formats/gml/index.js.map +1 -0
  192. package/dist/src/formats/gml/syntax.d.ts +186 -0
  193. package/dist/src/formats/gml/syntax.d.ts.map +1 -0
  194. package/dist/src/formats/gml/syntax.js +467 -0
  195. package/dist/src/formats/gml/syntax.js.map +1 -0
  196. package/dist/src/formats/graphml/constants.d.ts +169 -0
  197. package/dist/src/formats/graphml/constants.d.ts.map +1 -0
  198. package/dist/src/formats/graphml/constants.js +165 -0
  199. package/dist/src/formats/graphml/constants.js.map +1 -0
  200. package/dist/src/formats/graphml/exporter.d.ts +34 -0
  201. package/dist/src/formats/graphml/exporter.d.ts.map +1 -0
  202. package/dist/src/formats/graphml/exporter.js +1176 -0
  203. package/dist/src/formats/graphml/exporter.js.map +1 -0
  204. package/dist/src/formats/graphml/importer.d.ts +31 -0
  205. package/dist/src/formats/graphml/importer.d.ts.map +1 -0
  206. package/dist/src/formats/graphml/importer.js +1607 -0
  207. package/dist/src/formats/graphml/importer.js.map +1 -0
  208. package/dist/src/formats/graphml/index.d.ts +8 -0
  209. package/dist/src/formats/graphml/index.d.ts.map +1 -0
  210. package/dist/src/formats/graphml/index.js +8 -0
  211. package/dist/src/formats/graphml/index.js.map +1 -0
  212. package/dist/src/formats/graphml/tree.d.ts +72 -0
  213. package/dist/src/formats/graphml/tree.d.ts.map +1 -0
  214. package/dist/src/formats/graphml/tree.js +290 -0
  215. package/dist/src/formats/graphml/tree.js.map +1 -0
  216. package/dist/src/formats/json/dialect.d.ts +125 -0
  217. package/dist/src/formats/json/dialect.d.ts.map +1 -0
  218. package/dist/src/formats/json/dialect.js +262 -0
  219. package/dist/src/formats/json/dialect.js.map +1 -0
  220. package/dist/src/formats/json/exporter.d.ts +89 -0
  221. package/dist/src/formats/json/exporter.d.ts.map +1 -0
  222. package/dist/src/formats/json/exporter.js +1358 -0
  223. package/dist/src/formats/json/exporter.js.map +1 -0
  224. package/dist/src/formats/json/importer.d.ts +108 -0
  225. package/dist/src/formats/json/importer.d.ts.map +1 -0
  226. package/dist/src/formats/json/importer.js +1838 -0
  227. package/dist/src/formats/json/importer.js.map +1 -0
  228. package/dist/src/formats/json/index.d.ts +8 -0
  229. package/dist/src/formats/json/index.d.ts.map +1 -0
  230. package/dist/src/formats/json/index.js +8 -0
  231. package/dist/src/formats/json/index.js.map +1 -0
  232. package/dist/src/formats/neo4j/exporter.d.ts +68 -0
  233. package/dist/src/formats/neo4j/exporter.d.ts.map +1 -0
  234. package/dist/src/formats/neo4j/exporter.js +1055 -0
  235. package/dist/src/formats/neo4j/exporter.js.map +1 -0
  236. package/dist/src/formats/neo4j/header.d.ts +52 -0
  237. package/dist/src/formats/neo4j/header.d.ts.map +1 -0
  238. package/dist/src/formats/neo4j/header.js +131 -0
  239. package/dist/src/formats/neo4j/header.js.map +1 -0
  240. package/dist/src/formats/neo4j/importer.d.ts +73 -0
  241. package/dist/src/formats/neo4j/importer.d.ts.map +1 -0
  242. package/dist/src/formats/neo4j/importer.js +932 -0
  243. package/dist/src/formats/neo4j/importer.js.map +1 -0
  244. package/dist/src/formats/neo4j/index.d.ts +79 -0
  245. package/dist/src/formats/neo4j/index.d.ts.map +1 -0
  246. package/dist/src/formats/neo4j/index.js +83 -0
  247. package/dist/src/formats/neo4j/index.js.map +1 -0
  248. package/dist/src/formats/pajek/exporter.d.ts +58 -0
  249. package/dist/src/formats/pajek/exporter.d.ts.map +1 -0
  250. package/dist/src/formats/pajek/exporter.js +825 -0
  251. package/dist/src/formats/pajek/exporter.js.map +1 -0
  252. package/dist/src/formats/pajek/importer.d.ts +88 -0
  253. package/dist/src/formats/pajek/importer.d.ts.map +1 -0
  254. package/dist/src/formats/pajek/importer.js +1047 -0
  255. package/dist/src/formats/pajek/importer.js.map +1 -0
  256. package/dist/src/formats/pajek/index.d.ts +7 -0
  257. package/dist/src/formats/pajek/index.d.ts.map +1 -0
  258. package/dist/src/formats/pajek/index.js +7 -0
  259. package/dist/src/formats/pajek/index.js.map +1 -0
  260. package/dist/src/formats/pajek/syntax.d.ts +112 -0
  261. package/dist/src/formats/pajek/syntax.d.ts.map +1 -0
  262. package/dist/src/formats/pajek/syntax.js +269 -0
  263. package/dist/src/formats/pajek/syntax.js.map +1 -0
  264. package/dist/src/index.d.ts +35 -0
  265. package/dist/src/index.d.ts.map +1 -0
  266. package/dist/src/index.js +39 -0
  267. package/dist/src/index.js.map +1 -0
  268. package/dist/src/registry.d.ts +207 -0
  269. package/dist/src/registry.d.ts.map +1 -0
  270. package/dist/src/registry.js +481 -0
  271. package/dist/src/registry.js.map +1 -0
  272. package/dist/src/sniff.d.ts +104 -0
  273. package/dist/src/sniff.d.ts.map +1 -0
  274. package/dist/src/sniff.js +357 -0
  275. package/dist/src/sniff.js.map +1 -0
  276. package/dist/src/types.d.ts +238 -0
  277. package/dist/src/types.d.ts.map +1 -0
  278. package/dist/src/types.js +29 -0
  279. package/dist/src/types.js.map +1 -0
  280. package/dist/tsconfig.build.tsbuildinfo +1 -0
  281. package/package.json +122 -7
  282. package/src/children.ts +335 -0
  283. package/src/common/attributes.ts +520 -0
  284. package/src/common/codes.ts +153 -0
  285. package/src/common/declared-types.ts +374 -0
  286. package/src/common/direction.ts +518 -0
  287. package/src/common/escape.ts +231 -0
  288. package/src/common/export.ts +817 -0
  289. package/src/common/format.ts +111 -0
  290. package/src/common/ids.ts +176 -0
  291. package/src/common/input.ts +378 -0
  292. package/src/common/lists.ts +196 -0
  293. package/src/common/options.ts +377 -0
  294. package/src/common/report.ts +352 -0
  295. package/src/common/temporal.ts +302 -0
  296. package/src/common/text.ts +294 -0
  297. package/src/common/weights.ts +202 -0
  298. package/src/common/writer.ts +123 -0
  299. package/src/common/xml.ts +1053 -0
  300. package/src/formats/csv/exporter.ts +894 -0
  301. package/src/formats/csv/header.ts +172 -0
  302. package/src/formats/csv/importer.ts +1104 -0
  303. package/src/formats/csv/index.ts +88 -0
  304. package/src/formats/csv/records.ts +813 -0
  305. package/src/formats/csv/values.ts +224 -0
  306. package/src/formats/dot/exporter.ts +1014 -0
  307. package/src/formats/dot/importer.ts +1549 -0
  308. package/src/formats/dot/index.ts +7 -0
  309. package/src/formats/dot/names.ts +40 -0
  310. package/src/formats/dot/tokenizer.ts +384 -0
  311. package/src/formats/gexf/exporter.ts +1696 -0
  312. package/src/formats/gexf/importer.ts +2333 -0
  313. package/src/formats/gexf/index.ts +142 -0
  314. package/src/formats/gexf/schema.ts +361 -0
  315. package/src/formats/gml/exporter.ts +1404 -0
  316. package/src/formats/gml/importer.ts +1591 -0
  317. package/src/formats/gml/index.ts +128 -0
  318. package/src/formats/gml/syntax.ts +545 -0
  319. package/src/formats/graphml/constants.ts +225 -0
  320. package/src/formats/graphml/exporter.ts +1458 -0
  321. package/src/formats/graphml/importer.ts +2027 -0
  322. package/src/formats/graphml/index.ts +8 -0
  323. package/src/formats/graphml/tree.ts +318 -0
  324. package/src/formats/json/dialect.ts +317 -0
  325. package/src/formats/json/exporter.ts +1616 -0
  326. package/src/formats/json/importer.ts +2271 -0
  327. package/src/formats/json/index.ts +8 -0
  328. package/src/formats/neo4j/exporter.ts +1287 -0
  329. package/src/formats/neo4j/header.ts +156 -0
  330. package/src/formats/neo4j/importer.ts +1220 -0
  331. package/src/formats/neo4j/index.ts +116 -0
  332. package/src/formats/pajek/exporter.ts +1000 -0
  333. package/src/formats/pajek/importer.ts +1311 -0
  334. package/src/formats/pajek/index.ts +7 -0
  335. package/src/formats/pajek/syntax.ts +307 -0
  336. package/src/index.ts +244 -0
  337. package/src/registry.ts +617 -0
  338. package/src/sniff.ts +397 -0
  339. package/src/types.ts +262 -0
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The DOT / Graphviz subpath entry (`@graphty/graph-io/dot`, design section 8.2): the importer and
3
+ * exporter plugins with their option types and issue / loss codes.
4
+ */
5
+
6
+ export { DOT_LOSS, dotExporter, type DotExportOptions } from "./exporter.js";
7
+ export { DOT_ISSUE, dotImporter, type DotImportOptions } from "./importer.js";
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The names the DOT importer and exporter share: the format name, the column origin, the
3
+ * format-owned columns (design section 5.6: `graphty.` prefix) and the attribute names the format
4
+ * gives a fixed meaning to.
5
+ */
6
+
7
+ import { type ColumnOriginInput } from "@graphty/graph-format";
8
+
9
+ /** The format name. */
10
+ export const DOT_FORMAT = "dot";
11
+
12
+ /** The origin recorded on every column the importer declares. */
13
+ export const DOT_ORIGIN: ColumnOriginInput = Object.freeze({ format: DOT_FORMAT });
14
+
15
+ /** The bool node column marking a cluster's container node. */
16
+ export const CLUSTER_COLUMN = "graphty.cluster";
17
+
18
+ /** The u32 node column (role parent, refersTo node) holding a member's cluster. */
19
+ export const PARENT_COLUMN = "graphty.parent";
20
+
21
+ /** The string edge column (role sourcePort) holding an edge's tail port. */
22
+ export const SOURCE_PORT_COLUMN = "graphty.sourcePort";
23
+
24
+ /** The string edge column (role targetPort) holding an edge's head port. */
25
+ export const TARGET_PORT_COLUMN = "graphty.targetPort";
26
+
27
+ /** The attribute that is the display label (kept as text with role label). */
28
+ export const LABEL_ATTRIBUTE = "label";
29
+
30
+ /** The node attribute holding a position, mapped to the position role column (design section 5.2). */
31
+ export const POS_ATTRIBUTE = "pos";
32
+
33
+ /** The edge attribute cgraph uses as an edge's identity within its endpoints, mapped to the edge id role. */
34
+ export const KEY_ATTRIBUTE = "key";
35
+
36
+ /** The node attribute a `!` suffix of `pos` sets (Graphviz's own spelling of a pinned position). */
37
+ export const PIN_ATTRIBUTE = "pin";
38
+
39
+ /** The key under meta.extra holding the format's graph flags (`{ strict: true }`). */
40
+ export const DOT_META_KEY = "dot";
@@ -0,0 +1,384 @@
1
+ /**
2
+ * The DOT lexer (graphviz.org/doc/info/lang.html): IDs in their four spellings (an alphanumeric
3
+ * identifier, a numeral, a double-quoted string with `\"` as its only escape, backslash pairs
4
+ * consumed as units and backslash-newline as a line continuation, an HTML string in balanced
5
+ * angle brackets), the punctuation of the
6
+ * grammar, the two edge operators, the `+` of quoted-string concatenation, and the three comment
7
+ * forms (`//`, `/* ... *\/`, and a `#` line, which is C preprocessor output). Keywords are not
8
+ * distinguished here: the parser matches a bare id case-insensitively, since a quoted `"node"` is
9
+ * an ordinary id. Line numbers are 1-based and attached to every token for the report.
10
+ */
11
+
12
+ /** The kinds of token. */
13
+ type DotTokenKind = "id" | "punct" | "eof";
14
+
15
+ /** One token of a DOT document. */
16
+ export interface DotToken {
17
+ /** The kind. */
18
+ readonly kind: DotTokenKind;
19
+ /**
20
+ * The decoded text of an id (quotes removed, `\"` unescaped, an HTML string kept with its outer
21
+ * angle brackets), the punctuation itself for "punct" (`{ } [ ] ; , = : + -> --`), "" at the end.
22
+ */
23
+ readonly text: string;
24
+ /** Whether an id was double-quoted (a quoted keyword is an ordinary id). */
25
+ readonly quoted: boolean;
26
+ /** Whether an id was an HTML string. */
27
+ readonly html: boolean;
28
+ /** The 1-based line the token starts on. */
29
+ readonly line: number;
30
+ }
31
+
32
+ /** The error the lexer and parser throw for a grammar violation; the importer turns it into a fatal parse-error. */
33
+ export class DotSyntaxError extends Error {
34
+ /** The 1-based line of the violation. */
35
+ readonly line: number;
36
+
37
+ /**
38
+ * Create a syntax error.
39
+ * @param message - a plain-ASCII message
40
+ * @param line - the 1-based line
41
+ */
42
+ constructor(message: string, line: number) {
43
+ super(message);
44
+ this.name = "DotSyntaxError";
45
+ this.line = line;
46
+ }
47
+ }
48
+
49
+ const EOF_TOKEN_TEXT = "";
50
+
51
+ /**
52
+ * Whether a UTF-16 code unit may start a bare DOT identifier: a letter, an underscore or any code
53
+ * of 128 and above (the grammar's `\200-\377` range, read here as every non-ASCII unit).
54
+ * @param c - the code unit
55
+ * @returns true for an identifier start
56
+ */
57
+ function isIdentifierStart(c: number): boolean {
58
+ return (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c === 95 || c >= 128;
59
+ }
60
+
61
+ /**
62
+ * Whether a UTF-16 code unit may continue a bare DOT identifier.
63
+ * @param c - the code unit
64
+ * @returns true for an identifier character
65
+ */
66
+ function isIdentifierPart(c: number): boolean {
67
+ return isIdentifierStart(c) || isDigit(c);
68
+ }
69
+
70
+ /**
71
+ * Whether a code unit is an ASCII digit.
72
+ * @param c - the code unit
73
+ * @returns true for 0-9
74
+ */
75
+ function isDigit(c: number): boolean {
76
+ return c >= 48 && c <= 57;
77
+ }
78
+
79
+ /**
80
+ * A pull lexer over one DOT document with arbitrary lookahead.
81
+ */
82
+ export class DotTokenizer {
83
+ private readonly text: string;
84
+
85
+ private pos = 0;
86
+
87
+ private line = 1;
88
+
89
+ private readonly lookahead: DotToken[] = [];
90
+
91
+ private readonly onAmbiguity: ((numeral: string, line: number) => void) | null;
92
+
93
+ /**
94
+ * Create a lexer over a whole document.
95
+ * @param text - the DOT text (BOM already removed)
96
+ * @param onAmbiguity - called for a badly delimited numeral (`1e3`, which Graphviz splits into
97
+ * `1` and `e3` with a warning), with the numeral text and its line
98
+ */
99
+ constructor(text: string, onAmbiguity: ((numeral: string, line: number) => void) | null = null) {
100
+ this.text = text;
101
+ this.onAmbiguity = onAmbiguity;
102
+ }
103
+
104
+ /**
105
+ * Look at a token without consuming it.
106
+ * @param n - how far ahead (0 = the next token)
107
+ * @returns the token; an "eof" token past the end
108
+ */
109
+ peek(n = 0): DotToken {
110
+ while (this.lookahead.length <= n) {
111
+ this.lookahead.push(this.lex());
112
+ }
113
+ return this.lookahead[n];
114
+ }
115
+
116
+ /**
117
+ * Consume the next token.
118
+ * @returns the token; an "eof" token past the end
119
+ */
120
+ next(): DotToken {
121
+ const ahead = this.lookahead.shift();
122
+ return ahead ?? this.lex();
123
+ }
124
+
125
+ /**
126
+ * Produce the next token from the text.
127
+ * @returns the token
128
+ */
129
+ private lex(): DotToken {
130
+ this.skipTrivia();
131
+ const { text } = this;
132
+ if (this.pos >= text.length) {
133
+ return { kind: "eof", text: EOF_TOKEN_TEXT, quoted: false, html: false, line: this.line };
134
+ }
135
+ const start = this.pos;
136
+ const { line } = this;
137
+ const c = text.charCodeAt(start);
138
+ switch (c) {
139
+ case 0x7b: // {
140
+ case 0x7d: // }
141
+ case 0x5b: // [
142
+ case 0x5d: // ]
143
+ case 0x3b: // ;
144
+ case 0x2c: // ,
145
+ case 0x3d: // =
146
+ case 0x3a: // :
147
+ case 0x2b: // +
148
+ this.pos++;
149
+ return { kind: "punct", text: text[start], quoted: false, html: false, line };
150
+ case 0x2d: {
151
+ // - : an edge operator, or the sign of a numeral
152
+ const d = text.charCodeAt(start + 1);
153
+ if (d === 0x3e || d === 0x2d) {
154
+ this.pos += 2;
155
+ return { kind: "punct", text: text.slice(start, start + 2), quoted: false, html: false, line };
156
+ }
157
+ if (isDigit(d) || (d === 0x2e && isDigit(text.charCodeAt(start + 2)))) {
158
+ return this.numeral(start, line);
159
+ }
160
+ throw new DotSyntaxError("unexpected '-': not an edge operator or a numeral", line);
161
+ }
162
+ case 0x22:
163
+ return this.quoted(start, line);
164
+ case 0x3c:
165
+ return this.html(start, line);
166
+ default:
167
+ break;
168
+ }
169
+ if (isIdentifierStart(c)) {
170
+ let end = start + 1;
171
+ while (end < text.length && isIdentifierPart(text.charCodeAt(end))) {
172
+ end++;
173
+ }
174
+ this.pos = end;
175
+ return { kind: "id", text: text.slice(start, end), quoted: false, html: false, line };
176
+ }
177
+ if (isDigit(c) || (c === 0x2e && isDigit(text.charCodeAt(start + 1)))) {
178
+ return this.numeral(start, line);
179
+ }
180
+ throw new DotSyntaxError(`unexpected character ${JSON.stringify(text[start])}`, line);
181
+ }
182
+
183
+ /**
184
+ * Lex a numeral `-?(.[0-9]+ | [0-9]+(.[0-9]*)?)` starting at `start`.
185
+ * @param start - the offset of the first character (a sign, a digit or a dot)
186
+ * @param line - the line
187
+ * @returns the id token holding the numeral text
188
+ */
189
+ private numeral(start: number, line: number): DotToken {
190
+ const { text } = this;
191
+ let end = start;
192
+ if (text.charCodeAt(end) === 0x2d) {
193
+ end++;
194
+ }
195
+ while (end < text.length && isDigit(text.charCodeAt(end))) {
196
+ end++;
197
+ }
198
+ if (text.charCodeAt(end) === 0x2e) {
199
+ end++;
200
+ while (end < text.length && isDigit(text.charCodeAt(end))) {
201
+ end++;
202
+ }
203
+ }
204
+ this.pos = end;
205
+ const numeral = text.slice(start, end);
206
+ if (end < text.length && isIdentifierStart(text.charCodeAt(end)) && this.onAmbiguity !== null) {
207
+ // Graphviz: "syntax ambiguity - badly delimited number '1e' ... splits into two tokens"
208
+ this.onAmbiguity(numeral, line);
209
+ }
210
+ return { kind: "id", text: numeral, quoted: false, html: false, line };
211
+ }
212
+
213
+ /**
214
+ * Lex a double-quoted string starting at the opening quote: `\"` is the only escape, a
215
+ * backslash followed by a line break is a continuation (both removed), every other backslash is
216
+ * kept as written, and raw line breaks are allowed inside.
217
+ * @param start - the offset of the opening quote
218
+ * @param line - the line of the opening quote
219
+ * @returns the id token holding the decoded text
220
+ */
221
+ private quoted(start: number, line: number): DotToken {
222
+ const { text } = this;
223
+ let out = "";
224
+ let segment = start + 1;
225
+ let i = start + 1;
226
+ for (;;) {
227
+ if (i >= text.length) {
228
+ throw new DotSyntaxError("unterminated quoted string (missing closing quote)", line);
229
+ }
230
+ const c = text.charCodeAt(i);
231
+ if (c === 0x22) {
232
+ out += text.slice(segment, i);
233
+ this.pos = i + 1;
234
+ return { kind: "id", text: out, quoted: true, html: false, line };
235
+ }
236
+ if (c === 0x5c) {
237
+ const d = text.charCodeAt(i + 1);
238
+ if (d === 0x22) {
239
+ out += `${text.slice(segment, i)}"`;
240
+ i += 2;
241
+ segment = i;
242
+ continue;
243
+ }
244
+ if (d === 0x5c) {
245
+ // Graphviz's scanner consumes a backslash pair as one unit (both kept), so the
246
+ // second backslash never escapes a closing quote: "x\\" is the text x\\
247
+ i += 2;
248
+ continue;
249
+ }
250
+ if (d === 0x0a || d === 0x0d) {
251
+ out += text.slice(segment, i);
252
+ i += d === 0x0d && text.charCodeAt(i + 2) === 0x0a ? 3 : 2;
253
+ this.line++;
254
+ segment = i;
255
+ continue;
256
+ }
257
+ i++;
258
+ continue;
259
+ }
260
+ if (c === 0x0a || c === 0x0d) {
261
+ if (c === 0x0d && text.charCodeAt(i + 1) === 0x0a) {
262
+ i++;
263
+ }
264
+ this.line++;
265
+ }
266
+ i++;
267
+ }
268
+ }
269
+
270
+ /**
271
+ * Lex an HTML string starting at the opening angle bracket: the text up to the matching
272
+ * closing bracket, brackets balanced, kept verbatim including the outer pair.
273
+ * @param start - the offset of the opening bracket
274
+ * @param line - the line of the opening bracket
275
+ * @returns the id token holding the whole `<...>` text
276
+ */
277
+ private html(start: number, line: number): DotToken {
278
+ const { text } = this;
279
+ let depth = 0;
280
+ for (let i = start; i < text.length; i++) {
281
+ const c = text.charCodeAt(i);
282
+ if (c === 0x3c) {
283
+ depth++;
284
+ } else if (c === 0x3e) {
285
+ depth--;
286
+ if (depth === 0) {
287
+ this.pos = i + 1;
288
+ return { kind: "id", text: text.slice(start, i + 1), quoted: false, html: true, line };
289
+ }
290
+ } else if (c === 0x0a || (c === 0x0d && text.charCodeAt(i + 1) !== 0x0a)) {
291
+ this.line++;
292
+ }
293
+ }
294
+ throw new DotSyntaxError("unterminated HTML string (missing closing '>')", line);
295
+ }
296
+
297
+ /**
298
+ * Skip whitespace and comments, counting lines.
299
+ */
300
+ private skipTrivia(): void {
301
+ const { text } = this;
302
+ for (;;) {
303
+ if (this.pos >= text.length) {
304
+ return;
305
+ }
306
+ const c = text.charCodeAt(this.pos);
307
+ if (c === 0x0a) {
308
+ this.line++;
309
+ this.pos++;
310
+ this.skipPreprocessorLine();
311
+ continue;
312
+ }
313
+ if (c === 0x0d) {
314
+ this.line++;
315
+ this.pos += text.charCodeAt(this.pos + 1) === 0x0a ? 2 : 1;
316
+ this.skipPreprocessorLine();
317
+ continue;
318
+ }
319
+ if (c === 0x20 || c === 0x09 || c === 0x0b || c === 0x0c) {
320
+ this.pos++;
321
+ continue;
322
+ }
323
+ if (c === 0x2f) {
324
+ const d = text.charCodeAt(this.pos + 1);
325
+ if (d === 0x2f) {
326
+ this.skipToLineEnd();
327
+ continue;
328
+ }
329
+ if (d === 0x2a) {
330
+ this.skipBlockComment();
331
+ continue;
332
+ }
333
+ }
334
+ if (c === 0x23 && this.pos === 0) {
335
+ this.skipToLineEnd();
336
+ continue;
337
+ }
338
+ return;
339
+ }
340
+ }
341
+
342
+ /**
343
+ * Discard a `#` line when one starts at the current position (the start of a line).
344
+ */
345
+ private skipPreprocessorLine(): void {
346
+ if (this.text.charCodeAt(this.pos) === 0x23) {
347
+ this.skipToLineEnd();
348
+ }
349
+ }
350
+
351
+ /**
352
+ * Advance to the next line break without consuming it.
353
+ */
354
+ private skipToLineEnd(): void {
355
+ const { text } = this;
356
+ while (this.pos < text.length) {
357
+ const c = text.charCodeAt(this.pos);
358
+ if (c === 0x0a || c === 0x0d) {
359
+ return;
360
+ }
361
+ this.pos++;
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Skip a block comment starting at the current `/*`.
367
+ */
368
+ private skipBlockComment(): void {
369
+ const { text } = this;
370
+ const startLine = this.line;
371
+ let i = this.pos + 2;
372
+ for (; i < text.length; i++) {
373
+ const c = text.charCodeAt(i);
374
+ if (c === 0x2a && text.charCodeAt(i + 1) === 0x2f) {
375
+ this.pos = i + 2;
376
+ return;
377
+ }
378
+ if (c === 0x0a || (c === 0x0d && text.charCodeAt(i + 1) !== 0x0a)) {
379
+ this.line++;
380
+ }
381
+ }
382
+ throw new DotSyntaxError("unterminated block comment", startLine);
383
+ }
384
+ }