jade-lang 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 (326) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +23 -0
  3. data/LICENSE +21 -0
  4. data/README.md +386 -0
  5. data/exe/jade +6 -0
  6. data/lib/jade/ast/node.rb +44 -0
  7. data/lib/jade/ast/nodes.rb +35 -0
  8. data/lib/jade/ast/pretty_printer.rb +50 -0
  9. data/lib/jade/ast.rb +723 -0
  10. data/lib/jade/calendar/runtime.rb +15 -0
  11. data/lib/jade/cli/fmt.rb +96 -0
  12. data/lib/jade/cli/lsp.rb +13 -0
  13. data/lib/jade/cli/q.rb +113 -0
  14. data/lib/jade/cli.rb +43 -0
  15. data/lib/jade/clock/runtime.rb +13 -0
  16. data/lib/jade/codegen/boundary/cache.rb +94 -0
  17. data/lib/jade/codegen/boundary/specialized/list.rb +65 -0
  18. data/lib/jade/codegen/boundary/specialized/maybe.rb +40 -0
  19. data/lib/jade/codegen/boundary/specialized/record.rb +165 -0
  20. data/lib/jade/codegen/boundary/specialized/scalar.rb +67 -0
  21. data/lib/jade/codegen/boundary/specialized.rb +106 -0
  22. data/lib/jade/codegen/boundary.rb +189 -0
  23. data/lib/jade/codegen/constructor_reference.rb +18 -0
  24. data/lib/jade/codegen/context.rb +96 -0
  25. data/lib/jade/codegen/emitter.rb +81 -0
  26. data/lib/jade/codegen/function_call.rb +367 -0
  27. data/lib/jade/codegen/function_declaration.rb +199 -0
  28. data/lib/jade/codegen/helpers.rb +103 -0
  29. data/lib/jade/codegen/implementation.rb +178 -0
  30. data/lib/jade/codegen/inline.rb +89 -0
  31. data/lib/jade/codegen/inlines.rb +326 -0
  32. data/lib/jade/codegen/method_names.rb +54 -0
  33. data/lib/jade/codegen/pattern/constructor.rb +57 -0
  34. data/lib/jade/codegen/port_decoder.rb +77 -0
  35. data/lib/jade/codegen/pretty.rb +53 -0
  36. data/lib/jade/codegen/transforms/fold_shape.rb +222 -0
  37. data/lib/jade/codegen/transforms/self_call.rb +80 -0
  38. data/lib/jade/codegen/transforms/tail_call.rb +120 -0
  39. data/lib/jade/codegen/variant_declaration.rb +41 -0
  40. data/lib/jade/codegen.rb +400 -0
  41. data/lib/jade/compiler.rb +69 -0
  42. data/lib/jade/decode.rb +320 -0
  43. data/lib/jade/diagnostics/renderer.rb +121 -0
  44. data/lib/jade/diagnostics.rb +77 -0
  45. data/lib/jade/did_you_mean.rb +16 -0
  46. data/lib/jade/entry.rb +177 -0
  47. data/lib/jade/error.rb +72 -0
  48. data/lib/jade/formatter/accesses.rb +37 -0
  49. data/lib/jade/formatter/bindings.rb +29 -0
  50. data/lib/jade/formatter/body.rb +50 -0
  51. data/lib/jade/formatter/calls.rb +51 -0
  52. data/lib/jade/formatter/case_of.rb +31 -0
  53. data/lib/jade/formatter/case_of_branch.rb +59 -0
  54. data/lib/jade/formatter/collections.rb +78 -0
  55. data/lib/jade/formatter/declarations.rb +178 -0
  56. data/lib/jade/formatter/exposing.rb +48 -0
  57. data/lib/jade/formatter/function_declaration.rb +72 -0
  58. data/lib/jade/formatter/helper.rb +122 -0
  59. data/lib/jade/formatter/if_then_else.rb +64 -0
  60. data/lib/jade/formatter/infix_application.rb +69 -0
  61. data/lib/jade/formatter/lambda.rb +50 -0
  62. data/lib/jade/formatter/leaves.rb +111 -0
  63. data/lib/jade/formatter/module_node.rb +26 -0
  64. data/lib/jade/formatter/pattern.rb +61 -0
  65. data/lib/jade/formatter/type.rb +67 -0
  66. data/lib/jade/formatter.rb +38 -0
  67. data/lib/jade/frontend/comment_attacher.rb +121 -0
  68. data/lib/jade/frontend/desugaring/placeholder.rb +39 -0
  69. data/lib/jade/frontend/desugaring/resolved.rb +63 -0
  70. data/lib/jade/frontend/desugaring.rb +217 -0
  71. data/lib/jade/frontend/fixity_fixer.rb +209 -0
  72. data/lib/jade/frontend/forward_declaration/body.rb +30 -0
  73. data/lib/jade/frontend/forward_declaration/error/bad_import.rb +19 -0
  74. data/lib/jade/frontend/forward_declaration/error/exposed_type_not_found.rb +18 -0
  75. data/lib/jade/frontend/forward_declaration/error/exposed_value_not_found.rb +18 -0
  76. data/lib/jade/frontend/forward_declaration/error/module_not_found.rb +18 -0
  77. data/lib/jade/frontend/forward_declaration/error/private_type_expansion.rb +19 -0
  78. data/lib/jade/frontend/forward_declaration/error/tuple_arity_overflow.rb +25 -0
  79. data/lib/jade/frontend/forward_declaration/error/type_not_found.rb +29 -0
  80. data/lib/jade/frontend/forward_declaration/error/type_not_lowerable.rb +16 -0
  81. data/lib/jade/frontend/forward_declaration/error/unknown_extends_interface.rb +18 -0
  82. data/lib/jade/frontend/forward_declaration/error.rb +11 -0
  83. data/lib/jade/frontend/forward_declaration/function_declaration.rb +32 -0
  84. data/lib/jade/frontend/forward_declaration/helper.rb +91 -0
  85. data/lib/jade/frontend/forward_declaration/implementation.rb +63 -0
  86. data/lib/jade/frontend/forward_declaration/implementation_function.rb +39 -0
  87. data/lib/jade/frontend/forward_declaration/import_declaration.rb +115 -0
  88. data/lib/jade/frontend/forward_declaration/interface_declaration.rb +66 -0
  89. data/lib/jade/frontend/forward_declaration/interop_import_declaration.rb +99 -0
  90. data/lib/jade/frontend/forward_declaration/module.rb +98 -0
  91. data/lib/jade/frontend/forward_declaration/struct_declaration.rb +42 -0
  92. data/lib/jade/frontend/forward_declaration/type_declaration.rb +42 -0
  93. data/lib/jade/frontend/forward_declaration.rb +71 -0
  94. data/lib/jade/frontend/pattern_analysis/exhaustiveness.rb +65 -0
  95. data/lib/jade/frontend/pattern_analysis/matrix.rb +235 -0
  96. data/lib/jade/frontend/pattern_analysis.rb +40 -0
  97. data/lib/jade/frontend/semantic_analysis/assign.rb +20 -0
  98. data/lib/jade/frontend/semantic_analysis/body.rb +33 -0
  99. data/lib/jade/frontend/semantic_analysis/case_of.rb +19 -0
  100. data/lib/jade/frontend/semantic_analysis/case_of_branch.rb +20 -0
  101. data/lib/jade/frontend/semantic_analysis/char_literal.rb +14 -0
  102. data/lib/jade/frontend/semantic_analysis/constructor_reference.rb +64 -0
  103. data/lib/jade/frontend/semantic_analysis/error/circular_extends.rb +19 -0
  104. data/lib/jade/frontend/semantic_analysis/error/constant_not_callable.rb +24 -0
  105. data/lib/jade/frontend/semantic_analysis/error/constructor_not_found.rb +34 -0
  106. data/lib/jade/frontend/semantic_analysis/error/constructor_pattern_arity_mismatch.rb +24 -0
  107. data/lib/jade/frontend/semantic_analysis/error/duplicate_field.rb +18 -0
  108. data/lib/jade/frontend/semantic_analysis/error/duplicate_function_declaration.rb +25 -0
  109. data/lib/jade/frontend/semantic_analysis/error/duplicate_record_field.rb +19 -0
  110. data/lib/jade/frontend/semantic_analysis/error/invalid_list_rest_pattern.rb +21 -0
  111. data/lib/jade/frontend/semantic_analysis/error/kwargs_on_non_constructor.rb +17 -0
  112. data/lib/jade/frontend/semantic_analysis/error/missing_exposing_clause.rb +17 -0
  113. data/lib/jade/frontend/semantic_analysis/error/missing_extends_implementation.rb +21 -0
  114. data/lib/jade/frontend/semantic_analysis/error/missing_field.rb +20 -0
  115. data/lib/jade/frontend/semantic_analysis/error/missing_implementation_function.rb +19 -0
  116. data/lib/jade/frontend/semantic_analysis/error/module_not_found.rb +22 -0
  117. data/lib/jade/frontend/semantic_analysis/error/nested_task_port.rb +19 -0
  118. data/lib/jade/frontend/semantic_analysis/error/non_task_port.rb +22 -0
  119. data/lib/jade/frontend/semantic_analysis/error/orphan_implementation.rb +20 -0
  120. data/lib/jade/frontend/semantic_analysis/error/predicate_must_return_bool.rb +22 -0
  121. data/lib/jade/frontend/semantic_analysis/error/predicate_name_not_allowed.rb +25 -0
  122. data/lib/jade/frontend/semantic_analysis/error/shadowing_error.rb +22 -0
  123. data/lib/jade/frontend/semantic_analysis/error/type_args_mismatch.rb +25 -0
  124. data/lib/jade/frontend/semantic_analysis/error/type_param_required.rb +19 -0
  125. data/lib/jade/frontend/semantic_analysis/error/unbound_type_variable.rb +22 -0
  126. data/lib/jade/frontend/semantic_analysis/error/undefined_variable.rb +29 -0
  127. data/lib/jade/frontend/semantic_analysis/error/unknown_field.rb +20 -0
  128. data/lib/jade/frontend/semantic_analysis/error/unknown_implementation_function.rb +19 -0
  129. data/lib/jade/frontend/semantic_analysis/error/unused_interface_type_param.rb +24 -0
  130. data/lib/jade/frontend/semantic_analysis/error/value_not_exposed.rb +23 -0
  131. data/lib/jade/frontend/semantic_analysis/error/variable_not_found.rb +25 -0
  132. data/lib/jade/frontend/semantic_analysis/error.rb +40 -0
  133. data/lib/jade/frontend/semantic_analysis/function_call.rb +60 -0
  134. data/lib/jade/frontend/semantic_analysis/function_declaration.rb +58 -0
  135. data/lib/jade/frontend/semantic_analysis/grouping.rb +17 -0
  136. data/lib/jade/frontend/semantic_analysis/helper.rb +152 -0
  137. data/lib/jade/frontend/semantic_analysis/if_then_else.rb +20 -0
  138. data/lib/jade/frontend/semantic_analysis/implementation.rb +143 -0
  139. data/lib/jade/frontend/semantic_analysis/implementation_function.rb +16 -0
  140. data/lib/jade/frontend/semantic_analysis/import_declaration.rb +14 -0
  141. data/lib/jade/frontend/semantic_analysis/interface_declaration.rb +45 -0
  142. data/lib/jade/frontend/semantic_analysis/interop_import_declaration.rb +69 -0
  143. data/lib/jade/frontend/semantic_analysis/keyed_call/validation.rb +109 -0
  144. data/lib/jade/frontend/semantic_analysis/keyed_call.rb +88 -0
  145. data/lib/jade/frontend/semantic_analysis/lambda.rb +23 -0
  146. data/lib/jade/frontend/semantic_analysis/list.rb +17 -0
  147. data/lib/jade/frontend/semantic_analysis/literal.rb +23 -0
  148. data/lib/jade/frontend/semantic_analysis/member_access.rb +87 -0
  149. data/lib/jade/frontend/semantic_analysis/module_node.rb +27 -0
  150. data/lib/jade/frontend/semantic_analysis/pattern_binding.rb +27 -0
  151. data/lib/jade/frontend/semantic_analysis/pattern_constructor.rb +47 -0
  152. data/lib/jade/frontend/semantic_analysis/pattern_list.rb +33 -0
  153. data/lib/jade/frontend/semantic_analysis/pattern_literal.rb +17 -0
  154. data/lib/jade/frontend/semantic_analysis/pattern_record.rb +25 -0
  155. data/lib/jade/frontend/semantic_analysis/pattern_wildcard.rb +14 -0
  156. data/lib/jade/frontend/semantic_analysis/qualified_access.rb +14 -0
  157. data/lib/jade/frontend/semantic_analysis/record_access.rb +14 -0
  158. data/lib/jade/frontend/semantic_analysis/record_field.rb +17 -0
  159. data/lib/jade/frontend/semantic_analysis/record_literal.rb +21 -0
  160. data/lib/jade/frontend/semantic_analysis/record_update.rb +21 -0
  161. data/lib/jade/frontend/semantic_analysis/struct_declaration.rb +44 -0
  162. data/lib/jade/frontend/semantic_analysis/tuple.rb +17 -0
  163. data/lib/jade/frontend/semantic_analysis/type_declaration.rb +69 -0
  164. data/lib/jade/frontend/semantic_analysis/variable_reference.rb +27 -0
  165. data/lib/jade/frontend/semantic_analysis/variant_declaration.rb +18 -0
  166. data/lib/jade/frontend/semantic_analysis.rb +161 -0
  167. data/lib/jade/frontend/type_checking/canonicalize.rb +97 -0
  168. data/lib/jade/frontend/type_checking/constraints/deriving/decodable.rb +144 -0
  169. data/lib/jade/frontend/type_checking/constraints/deriving/encodable.rb +144 -0
  170. data/lib/jade/frontend/type_checking/constraints/deriving/eq.rb +265 -0
  171. data/lib/jade/frontend/type_checking/constraints/deriving/helpers.rb +59 -0
  172. data/lib/jade/frontend/type_checking/constraints/deriving.rb +28 -0
  173. data/lib/jade/frontend/type_checking/constraints.rb +101 -0
  174. data/lib/jade/frontend/type_checking/definition.rb +71 -0
  175. data/lib/jade/frontend/type_checking/env.rb +79 -0
  176. data/lib/jade/frontend/type_checking/error/case_of_branches_type_mismatch.rb +19 -0
  177. data/lib/jade/frontend/type_checking/error/derivation_failed.rb +21 -0
  178. data/lib/jade/frontend/type_checking/error/function_body_type_mismatch.rb +23 -0
  179. data/lib/jade/frontend/type_checking/error/function_call_type_mismatch.rb +37 -0
  180. data/lib/jade/frontend/type_checking/error/if_branch_type_mismatch.rb +19 -0
  181. data/lib/jade/frontend/type_checking/error/if_branches_type_mismatch.rb +18 -0
  182. data/lib/jade/frontend/type_checking/error/if_condition_type_mismatch.rb +17 -0
  183. data/lib/jade/frontend/type_checking/error/implementation_type_mismatch.rb +20 -0
  184. data/lib/jade/frontend/type_checking/error/list_item_type_mismatch.rb +19 -0
  185. data/lib/jade/frontend/type_checking/error/missing_implementation.rb +20 -0
  186. data/lib/jade/frontend/type_checking/error/missing_patterns.rb +26 -0
  187. data/lib/jade/frontend/type_checking/error/pattern_type_mismatch.rb +13 -0
  188. data/lib/jade/frontend/type_checking/error/port_not_decodable.rb +38 -0
  189. data/lib/jade/frontend/type_checking/error/record_access_type_mismatch.rb +14 -0
  190. data/lib/jade/frontend/type_checking/error/type_mismatch.rb +23 -0
  191. data/lib/jade/frontend/type_checking/error/unresolved_constraint.rb +20 -0
  192. data/lib/jade/frontend/type_checking/error.rb +18 -0
  193. data/lib/jade/frontend/type_checking/expected.rb +23 -0
  194. data/lib/jade/frontend/type_checking/generalization.rb +17 -0
  195. data/lib/jade/frontend/type_checking/generalizer.rb +38 -0
  196. data/lib/jade/frontend/type_checking/inference/assign.rb +58 -0
  197. data/lib/jade/frontend/type_checking/inference/body.rb +45 -0
  198. data/lib/jade/frontend/type_checking/inference/case_of.rb +102 -0
  199. data/lib/jade/frontend/type_checking/inference/constructor_reference.rb +21 -0
  200. data/lib/jade/frontend/type_checking/inference/function_call.rb +132 -0
  201. data/lib/jade/frontend/type_checking/inference/function_declaration.rb +70 -0
  202. data/lib/jade/frontend/type_checking/inference/grouping.rb +18 -0
  203. data/lib/jade/frontend/type_checking/inference/helpers.rb +34 -0
  204. data/lib/jade/frontend/type_checking/inference/if_then_else.rb +46 -0
  205. data/lib/jade/frontend/type_checking/inference/implementation.rb +150 -0
  206. data/lib/jade/frontend/type_checking/inference/import_declaration.rb +19 -0
  207. data/lib/jade/frontend/type_checking/inference/interface_declaration.rb +18 -0
  208. data/lib/jade/frontend/type_checking/inference/interop_import_declaration.rb +18 -0
  209. data/lib/jade/frontend/type_checking/inference/lambda.rb +87 -0
  210. data/lib/jade/frontend/type_checking/inference/list.rb +52 -0
  211. data/lib/jade/frontend/type_checking/inference/literal.rb +24 -0
  212. data/lib/jade/frontend/type_checking/inference/module.rb +18 -0
  213. data/lib/jade/frontend/type_checking/inference/pattern.rb +135 -0
  214. data/lib/jade/frontend/type_checking/inference/qualified_access.rb +23 -0
  215. data/lib/jade/frontend/type_checking/inference/record_access.rb +35 -0
  216. data/lib/jade/frontend/type_checking/inference/record_field.rb +19 -0
  217. data/lib/jade/frontend/type_checking/inference/record_literal.rb +24 -0
  218. data/lib/jade/frontend/type_checking/inference/record_update.rb +37 -0
  219. data/lib/jade/frontend/type_checking/inference/struct_declaration.rb +18 -0
  220. data/lib/jade/frontend/type_checking/inference/type_declaration.rb +18 -0
  221. data/lib/jade/frontend/type_checking/inference/variable_reference.rb +27 -0
  222. data/lib/jade/frontend/type_checking/inference.rb +27 -0
  223. data/lib/jade/frontend/type_checking/instantiation.rb +24 -0
  224. data/lib/jade/frontend/type_checking/loader.rb +80 -0
  225. data/lib/jade/frontend/type_checking/placeholder.rb +12 -0
  226. data/lib/jade/frontend/type_checking/port_resolution.rb +123 -0
  227. data/lib/jade/frontend/type_checking/result.rb +41 -0
  228. data/lib/jade/frontend/type_checking/scheme.rb +20 -0
  229. data/lib/jade/frontend/type_checking/state.rb +52 -0
  230. data/lib/jade/frontend/type_checking/substitution.rb +93 -0
  231. data/lib/jade/frontend/type_checking/unification.rb +282 -0
  232. data/lib/jade/frontend/type_checking/var_gen.rb +33 -0
  233. data/lib/jade/frontend/type_checking.rb +129 -0
  234. data/lib/jade/frontend/unused_analysis.rb +41 -0
  235. data/lib/jade/frontend/usage_analysis/reference_index.rb +53 -0
  236. data/lib/jade/frontend/usage_analysis.rb +195 -0
  237. data/lib/jade/frontend.rb +101 -0
  238. data/lib/jade/interop/boundary.rb +68 -0
  239. data/lib/jade/interop/error.rb +84 -0
  240. data/lib/jade/interop/lowering/error.rb +32 -0
  241. data/lib/jade/interop/lowering.rb +53 -0
  242. data/lib/jade/interop/runtime.rb +24 -0
  243. data/lib/jade/interop.rb +1 -0
  244. data/lib/jade/lexer.rb +189 -0
  245. data/lib/jade/lsp/converters.rb +542 -0
  246. data/lib/jade/lsp/handlers.rb +340 -0
  247. data/lib/jade/lsp/server.rb +63 -0
  248. data/lib/jade/lsp/snippets.rb +100 -0
  249. data/lib/jade/lsp/state.rb +25 -0
  250. data/lib/jade/lsp.rb +16 -0
  251. data/lib/jade/module_loader/cache.rb +56 -0
  252. data/lib/jade/module_loader/dependency_graph.rb +23 -0
  253. data/lib/jade/module_loader/dependency_resolver.rb +48 -0
  254. data/lib/jade/module_loader/normalize.rb +34 -0
  255. data/lib/jade/module_loader/topological_sort.rb +41 -0
  256. data/lib/jade/module_loader.rb +127 -0
  257. data/lib/jade/parsing/combinators.rb +291 -0
  258. data/lib/jade/parsing/error.rb +154 -0
  259. data/lib/jade/parsing/token.rb +12 -0
  260. data/lib/jade/parsing/type.rb +92 -0
  261. data/lib/jade/parsing.rb +674 -0
  262. data/lib/jade/port.rb +1 -0
  263. data/lib/jade/registry.rb +79 -0
  264. data/lib/jade/result.rb +121 -0
  265. data/lib/jade/runtime.rb +127 -0
  266. data/lib/jade/source.rb +62 -0
  267. data/lib/jade/stdlib/basics.rb +214 -0
  268. data/lib/jade/stdlib/bytes.rb +70 -0
  269. data/lib/jade/stdlib/calendar.rb +405 -0
  270. data/lib/jade/stdlib/char.rb +27 -0
  271. data/lib/jade/stdlib/clock.rb +342 -0
  272. data/lib/jade/stdlib/compiled.rb +48 -0
  273. data/lib/jade/stdlib/decode/params.rb +154 -0
  274. data/lib/jade/stdlib/decode.rb +315 -0
  275. data/lib/jade/stdlib/dict.rb +134 -0
  276. data/lib/jade/stdlib/encode.rb +143 -0
  277. data/lib/jade/stdlib/intrinsics.rb +280 -0
  278. data/lib/jade/stdlib/list.rb +214 -0
  279. data/lib/jade/stdlib/maybe.rb +73 -0
  280. data/lib/jade/stdlib/result.rb +131 -0
  281. data/lib/jade/stdlib/set.rb +123 -0
  282. data/lib/jade/stdlib/string.rb +65 -0
  283. data/lib/jade/stdlib/task.rb +55 -0
  284. data/lib/jade/stdlib/tuple.rb +21 -0
  285. data/lib/jade/stdlib.rb +112 -0
  286. data/lib/jade/symbol/anonymous_record.rb +7 -0
  287. data/lib/jade/symbol/base.rb +15 -0
  288. data/lib/jade/symbol/constructor.rb +11 -0
  289. data/lib/jade/symbol/derived_function.rb +5 -0
  290. data/lib/jade/symbol/function.rb +15 -0
  291. data/lib/jade/symbol/function_type.rb +7 -0
  292. data/lib/jade/symbol/implementation.rb +17 -0
  293. data/lib/jade/symbol/implementation_template.rb +13 -0
  294. data/lib/jade/symbol/interface.rb +11 -0
  295. data/lib/jade/symbol/interface_function.rb +18 -0
  296. data/lib/jade/symbol/interop_function.rb +22 -0
  297. data/lib/jade/symbol/lambda.rb +7 -0
  298. data/lib/jade/symbol/parser.rb +79 -0
  299. data/lib/jade/symbol/partial_application.rb +7 -0
  300. data/lib/jade/symbol/record_type.rb +8 -0
  301. data/lib/jade/symbol/stdlib_function.rb +15 -0
  302. data/lib/jade/symbol/stdlib_implementation.rb +7 -0
  303. data/lib/jade/symbol/struct.rb +15 -0
  304. data/lib/jade/symbol/type_application.rb +8 -0
  305. data/lib/jade/symbol/type_ref.rb +11 -0
  306. data/lib/jade/symbol/union.rb +15 -0
  307. data/lib/jade/symbol/value_ref.rb +15 -0
  308. data/lib/jade/symbol/variable.rb +7 -0
  309. data/lib/jade/symbol/variant.rb +11 -0
  310. data/lib/jade/symbol.rb +162 -0
  311. data/lib/jade/task.rb +103 -0
  312. data/lib/jade/tasks/rspec.rb +266 -0
  313. data/lib/jade/tasks.rb +70 -0
  314. data/lib/jade/type/anonymous_record.rb +33 -0
  315. data/lib/jade/type/application.rb +21 -0
  316. data/lib/jade/type/base.rb +9 -0
  317. data/lib/jade/type/constraint.rb +17 -0
  318. data/lib/jade/type/constructor.rb +19 -0
  319. data/lib/jade/type/function.rb +18 -0
  320. data/lib/jade/type/partial_application.rb +17 -0
  321. data/lib/jade/type/unit.rb +15 -0
  322. data/lib/jade/type/var.rb +21 -0
  323. data/lib/jade/type.rb +259 -0
  324. data/lib/jade/version.rb +3 -0
  325. data/lib/jade.rb +55 -0
  326. metadata +387 -0
@@ -0,0 +1,72 @@
1
+ module Jade
2
+ module Formatter
3
+ module FunctionDeclaration
4
+ extend self
5
+ extend Helper
6
+
7
+ def format(node, indent:, source:)
8
+ node => AST::FunctionDeclaration(
9
+ name:, params:, return_type:, body:
10
+ )
11
+
12
+ [
13
+ format_signature(name, params, return_type, indent, source:),
14
+ format_node(body, indent: indent + 1, source:),
15
+ "end".then(&and_indent(indent)),
16
+ ].join("\n")
17
+ end
18
+
19
+ private
20
+
21
+ # `def name(params) -> Return`. When the inline form is too long:
22
+ # - if return is a breakable record, break the record (params stay
23
+ # inline when the resulting header fits).
24
+ # - else if there are params, break params multi-line with
25
+ # `-> Type` on the close-paren line.
26
+ # - else live with the long line.
27
+ def format_signature(name, params, return_type, indent, source:)
28
+ return_str = format_type_atom(return_type)
29
+ params_inline = format_params_inline(params, source:)
30
+ inline = "def #{name}#{params_inline} -> #{return_str}"
31
+
32
+ return inline.then(&and_indent(indent)) unless too_long?(inline, indent)
33
+
34
+ broken_record = try_break_record_return(name, params_inline, return_type, indent)
35
+ return broken_record if broken_record
36
+
37
+ return inline.then(&and_indent(indent)) if params.empty?
38
+
39
+ format_params_multi(name, params, return_str, indent, source:)
40
+ end
41
+
42
+ def format_params_inline(params, source:)
43
+ return "" if params.empty?
44
+
45
+ params.map { format_node(it, source:) }.join(", ").then { "(#{it})" }
46
+ end
47
+
48
+ def try_break_record_return(name, params_inline, return_type, indent)
49
+ return nil unless Type.breakable_record?(return_type)
50
+
51
+ record_multi = Type.format_record_multiline(return_type, indent)
52
+ header = "def #{name}#{params_inline} -> #{record_multi.lines.first.chomp}"
53
+ return nil if too_long?(header, indent)
54
+
55
+ "def #{name}#{params_inline} -> #{record_multi}"
56
+ .then(&and_indent(indent))
57
+ end
58
+
59
+ def format_params_multi(name, params, return_str, indent, source:)
60
+ params_lines = params
61
+ .map { "#{format_node(it, source:)},".then(&and_indent(indent + 1)) }
62
+ .join("\n")
63
+
64
+ [
65
+ "def #{name}(".then(&and_indent(indent)),
66
+ params_lines,
67
+ ") -> #{return_str}".then(&and_indent(indent)),
68
+ ].join("\n")
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,122 @@
1
+ module Jade
2
+ module Formatter
3
+ # Mixin for per-node formatter modules. Provides:
4
+ # - INDENT, LINE_LIMIT constants
5
+ # - `format_node(node, indent:, source:)` — top-level dispatcher
6
+ # that prepends leading comments and appends a trailing one.
7
+ # - `and_indent(n)` — prefixes every line of a string with N levels of
8
+ # indent, leaving blank lines untouched.
9
+ # - `too_long?(line, indent)` — width check against LINE_LIMIT.
10
+ # - `format_delimited(...)` — the open/items/close pattern used by
11
+ # Tuple, List, and a few callers.
12
+ # - `format_pattern`, `format_type`, `format_exposing` — sibling
13
+ # walks delegated to their own modules.
14
+ module Helper
15
+ def format_node(node, indent: 0, source: nil)
16
+ leading = format_leading_comments(node, indent)
17
+ trailing = format_trailing_comment(node)
18
+
19
+ dispatch_for(node)
20
+ .format(node, indent:, source:)
21
+ .then { leading + it + trailing }
22
+ end
23
+
24
+ def dispatch_for(node)
25
+ case node
26
+ in AST::Module then ModuleNode
27
+ in AST::Body then Body
28
+ in AST::FunctionDeclaration then FunctionDeclaration
29
+ in AST::FunctionDeclarationParam then FunctionDeclarationParam
30
+ in AST::TypeDeclaration then TypeDeclaration
31
+ in AST::VariantDeclaration then VariantDeclaration
32
+ in AST::StructDeclaration then StructDeclaration
33
+ in AST::ImportDeclaration then ImportDeclaration
34
+ in AST::InteropImportDeclaration then InteropImportDeclaration
35
+ in AST::InteropFunction then InteropFunction
36
+ in AST::InterfaceDeclaration then InterfaceDeclaration
37
+ in AST::InterfaceFunctionDecl then InterfaceFunctionDecl
38
+ in AST::Assign then Assign
39
+ in AST::Bind then Bind
40
+ in AST::Implementation then Implementation
41
+ in AST::ImplementationFunction then ImplementationFunction
42
+ in AST::IfThenElse then IfThenElse
43
+ in AST::CaseOf then CaseOf
44
+ in AST::CaseOfBranch then CaseOfBranch
45
+ in AST::Lambda then Lambda
46
+ in AST::InfixApplication then InfixApplication
47
+ in AST::FunctionCall then FunctionCall
48
+ in AST::KeyedCall then KeyedCall
49
+ in AST::Placeholder then Placeholder
50
+ in AST::MemberAccess then MemberAccess
51
+ in AST::QualifiedAccess then QualifiedAccess
52
+ in AST::RecordAccess then RecordAccess
53
+ in AST::RecordAccessSugar then RecordAccessSugar
54
+ in AST::RecordUpdateSugar then RecordUpdateSugar
55
+ in AST::Grouping then Grouping
56
+ in AST::Tuple then Tuple
57
+ in AST::List then List
58
+ in AST::RecordLiteral then RecordLiteral
59
+ in AST::RecordUpdate then RecordUpdate
60
+ in AST::VariableReference then VariableReference
61
+ in AST::ConstructorReference then ConstructorReference
62
+ in AST::CharLiteral then CharLiteral
63
+ in AST::Literal then Literal
64
+ end
65
+ end
66
+
67
+ def and_indent(indent)
68
+ ->(str) {
69
+ prefix = INDENT * indent
70
+ str.lines.map { |line| line == "\n" ? line : "#{prefix}#{line}" }.join
71
+ }
72
+ end
73
+
74
+ def too_long?(line, indent)
75
+ (INDENT * indent).length + line.length > LINE_LIMIT
76
+ end
77
+
78
+ def format_leading_comments(node, indent)
79
+ return "" if node.leading_comments.empty?
80
+
81
+ node.leading_comments
82
+ .map { |tok| tok.value.then(&and_indent(indent)) }
83
+ .join("\n")
84
+ .then { it + "\n" }
85
+ end
86
+
87
+ def format_trailing_comment(node)
88
+ return "" if node.trailing_comments.empty?
89
+
90
+ " #{node.trailing_comments.first.value}"
91
+ end
92
+
93
+ # Generic open/sep/close formatter shared by Tuple and List. Inline
94
+ # when it fits and no trailing-comma hint; multi-line otherwise.
95
+ def format_delimited(strs, open, close, trailing_comma, indent)
96
+ inline = "#{open}#{strs.join(', ')}#{close}"
97
+ if trailing_comma || too_long?(inline, indent)
98
+ inner = strs.map { "#{it.then(&and_indent(indent + 1))}," }.join("\n")
99
+ "#{INDENT * indent}#{open}\n#{inner}\n#{INDENT * indent}#{close}"
100
+ else
101
+ inline.then(&and_indent(indent))
102
+ end
103
+ end
104
+
105
+ def format_pattern(node, source: nil)
106
+ Pattern.format(node, source:)
107
+ end
108
+
109
+ def format_type(node)
110
+ Type.format(node)
111
+ end
112
+
113
+ def format_type_atom(node)
114
+ Type.format_atom(node)
115
+ end
116
+
117
+ def format_exposing(node, indent: 0)
118
+ Exposing.format(node, indent:)
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,64 @@
1
+ module Jade
2
+ module Formatter
3
+ module IfThenElse
4
+ extend self
5
+ extend Helper
6
+
7
+ def format(node, indent:, source:)
8
+ node => AST::IfThenElse(condition:, if_branch:, else_branch:)
9
+
10
+ cond_str = format_node(condition, source:)
11
+ try_inline_ternary(cond_str, if_branch, else_branch, indent, source:) ||
12
+ format_block(cond_str, if_branch, else_branch, indent, source:)
13
+ end
14
+
15
+ # Returns nil when block form is needed: either branch is multi-
16
+ # statement / has a leading comment, the combined line is too long,
17
+ # or either single-expression branch already formats across multiple
18
+ # lines (e.g. a `|>` ladder).
19
+ def try_inline_ternary(cond_str, if_branch, else_branch, indent, source:)
20
+ return nil if block_form?(if_branch, else_branch)
21
+
22
+ if_str = single_branch_expr(if_branch, source:)
23
+ else_str = single_branch_expr(else_branch, source:)
24
+ inline = "#{cond_str} ? #{if_str} : #{else_str}"
25
+
26
+ return nil if if_str.include?("\n") || else_str.include?("\n")
27
+ return nil if too_long?(inline, indent)
28
+
29
+ inline.then(&and_indent(indent))
30
+ end
31
+
32
+ def format_block(cond_str, if_branch, else_branch, indent, source:)
33
+ [
34
+ "if #{cond_str} then".then(&and_indent(indent)),
35
+ format_node(if_branch, indent: indent + 1, source:),
36
+ "else".then(&and_indent(indent)),
37
+ format_node(else_branch, indent: indent + 1, source:),
38
+ "end".then(&and_indent(indent)),
39
+ ].join("\n")
40
+ end
41
+
42
+ # Block form needed whenever a branch has more than one expression
43
+ # or a leading comment that the ternary form would drop.
44
+ def block_form?(if_branch, else_branch)
45
+ multi?(if_branch) || multi?(else_branch)
46
+ end
47
+
48
+ def multi?(body)
49
+ body.expressions.length > 1 ||
50
+ !body.leading_comments.empty? ||
51
+ !body.expressions.first.leading_comments.empty?
52
+ end
53
+
54
+ # Format a body known to hold a single expression with no leading
55
+ # comments. Called only after `block_form?` is false — the raise is
56
+ # an invariant guard, not a user-facing error.
57
+ def single_branch_expr(body, source:)
58
+ raise "formatter invariant: single-expr body expected" if multi?(body)
59
+
60
+ format_node(body.expressions.first, source:)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,69 @@
1
+ module Jade
2
+ module Formatter
3
+ module InfixApplication
4
+ extend self
5
+ extend Helper
6
+
7
+ def format(node, indent:, source:)
8
+ node => AST::InfixApplication(left:, operator:, right:)
9
+
10
+ case operator.value
11
+ when '|>'
12
+ format_pipe_chain(node, indent, source:)
13
+ when '++'
14
+ format_concat_chain(node, indent, source:)
15
+ else
16
+ "#{format_node(left, source:)} #{operator.value} #{format_node(right, source:)}"
17
+ .then(&and_indent(indent))
18
+ end
19
+ end
20
+
21
+ # `|>` chains of 3 or more arms always render as a ladder; the
22
+ # vertical shape reads as "transform stage by stage". Two-arm
23
+ # chains stay inline unless they bust the line budget.
24
+ def format_pipe_chain(node, indent, source:)
25
+ chain = collect_chain(node, '|>')
26
+ inline = chain.map { format_node(it, source:) }.join(' |> ')
27
+
28
+ if chain.length > 2 || too_long?(inline, indent)
29
+ format_ladder(chain, '|>', indent, source:)
30
+ else
31
+ inline.then(&and_indent(indent))
32
+ end
33
+ end
34
+
35
+ # `++` chains stay inline when they fit, ladder when they don't.
36
+ def format_concat_chain(node, indent, source:)
37
+ chain = collect_chain(node, '++')
38
+ inline = chain.map { format_node(it, source:) }.join(' ++ ')
39
+
40
+ if chain.length > 1 && too_long?(inline, indent)
41
+ format_ladder(chain, '++', indent, source:)
42
+ else
43
+ inline.then(&and_indent(indent))
44
+ end
45
+ end
46
+
47
+ # Walk a left-associative chain and return the operands in order
48
+ # (`a op b op c` → [a, b, c]).
49
+ def collect_chain(node, op)
50
+ case node
51
+ in AST::InfixApplication(left:, operator: AST::InfixOperator(value: ^op), right:)
52
+ collect_chain(left, op) + [right]
53
+ else
54
+ [node]
55
+ end
56
+ end
57
+
58
+ # Emit a chain ladder: head on its own line, each subsequent
59
+ # operand prefixed by `op` indented one level deeper.
60
+ def format_ladder(chain, op, indent, source:)
61
+ cont = INDENT * (indent + 1)
62
+ head = format_node(chain.first, indent:, source:)
63
+ tail = chain[1..].map { "#{cont}#{op} #{format_node(it, source:)}" }
64
+
65
+ ([head] + tail).join("\n")
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,50 @@
1
+ module Jade
2
+ module Formatter
3
+ module Lambda
4
+ extend self
5
+ extend Helper
6
+
7
+ # Atoms (and a few near-atoms) that read fine on a single line
8
+ # next to the lambda head. Everything else forces the multi-line
9
+ # `{ body }` shape.
10
+ INLINE_BODY = [
11
+ AST::Literal, AST::CharLiteral, AST::VariableReference,
12
+ AST::ConstructorReference, AST::FunctionCall, AST::RecordAccess,
13
+ AST::MemberAccess, AST::InfixApplication, AST::RecordLiteral,
14
+ AST::List, AST::Tuple, AST::Grouping, AST::RecordUpdate,
15
+ AST::RecordUpdateSugar, AST::RecordAccessSugar,
16
+ ].freeze
17
+
18
+ def format(node, indent:, source:)
19
+ node => AST::Lambda(params:, body:)
20
+
21
+ head = format_head(params)
22
+
23
+ if inline_body?(body)
24
+ "#{head} { #{format_node(body.expressions.first, source:)} }"
25
+ .then(&and_indent(indent))
26
+ else
27
+ [
28
+ "#{head} {".then(&and_indent(indent)),
29
+ format_node(body, indent: indent + 1, source:),
30
+ "}".then(&and_indent(indent)),
31
+ ].join("\n")
32
+ end
33
+ end
34
+
35
+ def format_head(params)
36
+ return "->" if params.empty?
37
+
38
+ params
39
+ .map { format_pattern(it) }
40
+ .join(', ')
41
+ .then { "(#{it}) ->" }
42
+ end
43
+
44
+ def inline_body?(body)
45
+ body.expressions.length == 1 &&
46
+ INLINE_BODY.any? { body.expressions.first.is_a?(it) }
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,111 @@
1
+ module Jade
2
+ module Formatter
3
+ # Simple leaf nodes: each is at most a one-liner of formatting logic.
4
+ # Grouped here so the per-node directory doesn't drown in trivia.
5
+
6
+ module VariableReference
7
+ extend self
8
+ extend Helper
9
+
10
+ def format(node, indent:, source:)
11
+ node.name.then(&and_indent(indent))
12
+ end
13
+ end
14
+
15
+ module ConstructorReference
16
+ extend self
17
+ extend Helper
18
+
19
+ def format(node, indent:, source:)
20
+ node.name.then(&and_indent(indent))
21
+ end
22
+ end
23
+
24
+ module CharLiteral
25
+ extend self
26
+ extend Helper
27
+
28
+ def format(node, indent:, source:)
29
+ "'#{node.value}'".then(&and_indent(indent))
30
+ end
31
+ end
32
+
33
+ module Literal
34
+ extend self
35
+ extend Helper
36
+
37
+ def format(node, indent:, source:)
38
+ case node.value
39
+ in Integer | Float then node.value.to_s
40
+ in TrueClass then "True"
41
+ in FalseClass then "False"
42
+ in String then node.value.inspect
43
+ end
44
+ .then(&and_indent(indent))
45
+ end
46
+ end
47
+
48
+ module Placeholder
49
+ extend self
50
+ extend Helper
51
+
52
+ def format(node, indent:, source:)
53
+ "_".then(&and_indent(indent))
54
+ end
55
+ end
56
+
57
+ module RecordAccessSugar
58
+ extend self
59
+ extend Helper
60
+
61
+ def format(node, indent:, source:)
62
+ ".#{node.field_key}".then(&and_indent(indent))
63
+ end
64
+ end
65
+
66
+ module RecordUpdateSugar
67
+ extend self
68
+ extend Helper
69
+
70
+ def format(node, indent:, source:)
71
+ ".#{node.field_key}=".then(&and_indent(indent))
72
+ end
73
+ end
74
+
75
+ module Grouping
76
+ extend self
77
+ extend Helper
78
+
79
+ def format(node, indent:, source:)
80
+ "(#{format_node(node.expression, source:)})".then(&and_indent(indent))
81
+ end
82
+ end
83
+
84
+ module FunctionDeclarationParam
85
+ extend self
86
+ extend Helper
87
+
88
+ def format(node, indent:, source:)
89
+ "#{node.name}: #{format_type(node.type)}"
90
+ end
91
+ end
92
+
93
+ module InteropFunction
94
+ extend self
95
+ extend Helper
96
+
97
+ def format(node, indent:, source:)
98
+ "#{node.name} : #{format_type(node.type)}"
99
+ end
100
+ end
101
+
102
+ module InterfaceFunctionDecl
103
+ extend self
104
+ extend Helper
105
+
106
+ def format(node, indent:, source:)
107
+ "#{node.name} : #{format_type(node.type)}".then(&and_indent(indent))
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,26 @@
1
+ module Jade
2
+ module Formatter
3
+ module ModuleNode
4
+ extend self
5
+ extend Helper
6
+
7
+ def format(node, indent:, source:)
8
+ node => AST::Module(name:, exposing:, body:)
9
+
10
+ chunks = body.expressions
11
+ .chunk_while { |a, b|
12
+ (a in AST::ImportDeclaration) && (b in AST::ImportDeclaration)
13
+ }
14
+ .map { |group|
15
+ group.map { format_node(it, indent:, source:) }.join("\n")
16
+ }
17
+
18
+ header = "module #{name} #{format_exposing(exposing)}"
19
+ # Two blank lines between top-level chunks so def-to-def boundaries
20
+ # don't blur with blank lines inside a def body. Module header to
21
+ # the first chunk stays one blank line.
22
+ chunks.empty? ? header : "#{header}\n\n#{chunks.join("\n\n\n")}"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,61 @@
1
+ module Jade
2
+ module Formatter
3
+ module Pattern
4
+ extend self
5
+ extend Helper
6
+
7
+ def format(node, source: nil)
8
+ case node
9
+ in AST::Pattern::Wildcard
10
+ "_"
11
+
12
+ in AST::Pattern::Literal(literal:)
13
+ format_node(literal, source:)
14
+
15
+ in AST::Pattern::Binding(name:)
16
+ name
17
+
18
+ in AST::Pattern::Constructor(constructor:, patterns:)
19
+ name = format_node(constructor, source:)
20
+
21
+ if patterns.nil? || patterns.empty?
22
+ name
23
+ else
24
+ patterns
25
+ .map { format(it) }
26
+ .join(', ')
27
+ .then { "#{name}(#{it})" }
28
+ end
29
+
30
+ in AST::Pattern::Record(fields:)
31
+ fields
32
+ .map { format(it) }
33
+ .join(", ")
34
+ .then { "{ #{it} }" }
35
+
36
+ in AST::Pattern::RecordField(name:, pattern: AST::Pattern::Binding(name: ^name))
37
+ "#{name}:"
38
+
39
+ in AST::Pattern::RecordField(name:, pattern:)
40
+ "#{name}: #{format(pattern)}"
41
+
42
+ in AST::Pattern::Tuple(patterns:)
43
+ patterns
44
+ .map { format(it) }
45
+ .join(', ')
46
+ .then { "(#{it})" }
47
+
48
+ in AST::Pattern::List(patterns:, rest:)
49
+ heads = patterns.map { format(it) }.join(', ')
50
+ tail = case rest
51
+ in AST::Pattern::Binding(name:) then " | #{name}"
52
+ in AST::Pattern::Wildcard then " | _"
53
+ in nil then ""
54
+ end
55
+
56
+ "[#{heads}#{tail}]"
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,67 @@
1
+ module Jade
2
+ module Formatter
3
+ module Type
4
+ extend self
5
+ extend Helper
6
+
7
+ def format(node)
8
+ case node
9
+ in AST::TypeName(type:)
10
+ type
11
+
12
+ in AST::QualifiedTypeName(path:)
13
+ path.join(".")
14
+
15
+ in AST::TypeVar(type:)
16
+ type
17
+
18
+ in AST::TypeApplication(constructor:, args:)
19
+ if args.empty?
20
+ format(constructor)
21
+ else
22
+ args
23
+ .map { format(it) }
24
+ .join(', ')
25
+ .then { "#{format(constructor)}(#{it})" }
26
+ end
27
+
28
+ in AST::TypeFunction(params:, return_type:)
29
+ params_str = params.empty? ?
30
+ "()" :
31
+ params.map { format_atom(it) }.join(', ')
32
+
33
+ "#{params_str} -> #{format_atom(return_type)}"
34
+
35
+ in AST::TypeRecord(fields:, row_var:)
36
+ fields_str = fields.map { |k, v| "#{k}: #{format(v)}" }.join(", ")
37
+ row_prefix = row_var ? "#{row_var.name} | " : ""
38
+
39
+ "{ #{row_prefix}#{fields_str} }"
40
+
41
+ in AST::TypeTuple(items:)
42
+ items.map { format(it) }.join(', ').then { "(#{it})" }
43
+ end
44
+ end
45
+
46
+ # Wrap a function type in `(...)` so nested arrows stay unambiguous —
47
+ # the type parser only accepts a `type_atom` on either side of `->`.
48
+ def format_atom(node)
49
+ node.is_a?(AST::TypeFunction) ? "(#{format(node)})" : format(node)
50
+ end
51
+
52
+ def breakable_record?(type)
53
+ type.is_a?(AST::TypeRecord) && type.fields.size > 1
54
+ end
55
+
56
+ def format_record_multiline(type, indent)
57
+ type => AST::TypeRecord(fields:, row_var:)
58
+ open = row_var ? "{ #{row_var.name} |" : "{"
59
+ fields_str = fields
60
+ .map { |k, v| "#{k}: #{format(v)},".then(&and_indent(indent + 1)) }
61
+ .join("\n")
62
+
63
+ "#{open}\n#{fields_str}\n#{INDENT * indent}}"
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,38 @@
1
+ require 'jade/formatter/helper'
2
+ require 'jade/formatter/pattern'
3
+ require 'jade/formatter/type'
4
+ require 'jade/formatter/exposing'
5
+ require 'jade/formatter/leaves'
6
+ require 'jade/formatter/accesses'
7
+ require 'jade/formatter/bindings'
8
+ require 'jade/formatter/calls'
9
+ require 'jade/formatter/collections'
10
+ require 'jade/formatter/declarations'
11
+ require 'jade/formatter/module_node'
12
+ require 'jade/formatter/body'
13
+ require 'jade/formatter/function_declaration'
14
+ require 'jade/formatter/lambda'
15
+ require 'jade/formatter/infix_application'
16
+ require 'jade/formatter/if_then_else'
17
+ require 'jade/formatter/case_of'
18
+ require 'jade/formatter/case_of_branch'
19
+
20
+ module Jade
21
+ module Formatter
22
+ extend self
23
+ extend Helper
24
+
25
+ # Constants live at the top-level module so unqualified references
26
+ # resolve via Ruby's lexical-scope constant lookup from any nested
27
+ # per-node module (which only `extend`s Helper — `extend` doesn't
28
+ # bring constants in).
29
+ INDENT = " "
30
+ LINE_LIMIT = 80
31
+
32
+ def format(node, comments:, source:, indent: 0)
33
+ Frontend::CommentAttacher
34
+ .attach(node, comments, source)
35
+ .then { format_node(it, indent:, source:) }
36
+ end
37
+ end
38
+ end