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,178 @@
1
+ module Jade
2
+ module Codegen
3
+ module Implementation
4
+ extend self
5
+ extend Helpers
6
+
7
+ def generate(node, registry)
8
+ node => AST::Implementation(symbol:)
9
+
10
+ [
11
+ generate_defs(node, registry),
12
+ generate_registrations_for(node, registry),
13
+ operator_impl_or_empty(node, registry, symbol.interface.qualified_name),
14
+ ]
15
+ .reject(&:empty?)
16
+ .join(Pretty.newline(2))
17
+ end
18
+
19
+ def operator_impl_or_empty(node, registry, iface_qname)
20
+ MethodNames.operator_interface?(iface_qname) \
21
+ ? generate_operator_impl(node, registry)
22
+ : ""
23
+ end
24
+
25
+ def generate_operator_impl(node, registry)
26
+ node => AST::Implementation(functions:, symbol:)
27
+
28
+ ruby_classes_for_type(symbol.type, registry).then { |ruby_classes|
29
+ next "" if ruby_classes.empty?
30
+
31
+ method_bodies_for(node, registry)
32
+ .then { it.empty? ? "" : class_reopens(ruby_classes, it) }
33
+ }
34
+ end
35
+
36
+ # Just the method-def strings (no surrounding `class ... end`). Used
37
+ # by `Codegen.collect_dispatched_methods` to gather impl methods for
38
+ # inlining into the type's `Data.define do ... end` block.
39
+ def method_bodies_for(node, registry)
40
+ node => AST::Implementation(functions:, symbol:)
41
+
42
+ [
43
+ *functions.filter_map { generate_operator_method(it, symbol, registry) },
44
+ *comparable_derivations(symbol.interface.qualified_name),
45
+ ]
46
+ end
47
+
48
+ def class_reopens(ruby_classes, method_defs)
49
+ method_defs
50
+ .join(Pretty.newline(2))
51
+ .then { |body| ruby_classes.map { Pretty.block("class #{it}", body) } }
52
+ .join(Pretty.newline(2))
53
+ end
54
+
55
+ def generate_operator_method(impl_fn, impl_sym, registry)
56
+ impl_fn => AST::ImplementationFunction(name: fn_name, fn:)
57
+
58
+ MethodNames
59
+ .interface_method(impl_sym.interface.qualified_name, fn_name)
60
+ &.then { |ruby_method| operator_method_body(ruby_method, fn, impl_sym, fn_name, registry) }
61
+ end
62
+
63
+ def operator_method_body(ruby_method, fn, impl_sym, fn_name, registry)
64
+ case fn
65
+ in AST::Lambda(params: [first_param, *rest_params], body:)
66
+ return nil unless simple_lambda_param?(first_param)
67
+ return nil unless rest_params.all? { simple_lambda_param?(it) }
68
+
69
+ rest_str = rest_params.map { generate_node(it, registry) }.join(', ')
70
+ first_name =
71
+ case first_param
72
+ in AST::Pattern::Binding(name:) then name
73
+ else nil
74
+ end
75
+
76
+ Codegen
77
+ .with_self_var_name(first_name) { generate_node(body, registry) }
78
+ .then { Pretty.block("def #{ruby_method}(#{rest_str})", it) }
79
+
80
+
81
+ # Operator-interface fns are all binary, so `(other)` is the signature.
82
+ in AST::VariableReference | AST::FunctionCall
83
+ impl_sym
84
+ .functions[fn_name]
85
+ .then { it.is_a?(Symbol::ValueRef) ? it : nil }
86
+ &.then { "::#{to_qualified(it.module_name)}::Internal.#{it.name}" }
87
+ &.then { Pretty.block("def #{ruby_method}(other)", "#{it}(self, other)") }
88
+ end
89
+ end
90
+
91
+ # `(Pepe(id), other) -> { ... }` would rebind a destructured pattern to
92
+ # `self` — invalid Ruby. Fall back to the dispatch-table path.
93
+ def simple_lambda_param?(pattern)
94
+ pattern in AST::Pattern::Binding | AST::Pattern::Wildcard
95
+ end
96
+
97
+ COMPARABLE_DERIVATIONS = [
98
+ "def <(other); compare(other) in Jade::Basics::LT; end",
99
+ "def >(other); compare(other) in Jade::Basics::GT; end",
100
+ "def <=(other); !(compare(other) in Jade::Basics::GT); end",
101
+ "def >=(other); !(compare(other) in Jade::Basics::LT); end",
102
+ ].freeze
103
+
104
+ def comparable_derivations(iface_qname)
105
+ iface_qname == 'Basics.Comparable' ? COMPARABLE_DERIVATIONS : []
106
+ end
107
+
108
+ def generate_defs(node, registry)
109
+ node => AST::Implementation(interface:, applied_type:, functions:)
110
+
111
+ type_name =
112
+ case applied_type.constructor
113
+ in AST::TypeName(type:) then type
114
+ in AST::QualifiedTypeName(path:) then path.last
115
+ end
116
+
117
+ functions
118
+ .filter_map { generate_function(it, registry, interface, type_name) }
119
+ .join(Pretty.newline(2))
120
+ end
121
+
122
+ def generate_registrations_for(node, registry)
123
+ node => AST::Implementation(symbol:)
124
+ generate_registrations(symbol, registry)
125
+ end
126
+
127
+ private
128
+
129
+ # Emits Runtime.register_impl calls for each Ruby class that values of
130
+ # the impl's type may have at runtime. The registered functions are
131
+ # the impl's public wrappers — for impls on parameterised types like
132
+ # `Encoder(Maybe(a))`, the wrapper does the inner-dict unboxing
133
+ # internally (see FunctionDeclaration#wrapper), so dynamic dispatch
134
+ # via threaded dicts lands in the right place.
135
+ def generate_registrations(symbol, registry)
136
+ ruby_classes = ruby_classes_for_type(symbol.type, registry)
137
+ return "" if ruby_classes.empty?
138
+
139
+ iface_qname = symbol.interface.qualified_name
140
+
141
+ # Lambda thunk defers the `Internal.fn` lookup until call time —
142
+ # register_impl runs at module load, before `Internal` is defined.
143
+ fn_map = symbol.functions.filter_map { |fn_name, ref|
144
+ next unless ref.is_a?(Symbol::ValueRef)
145
+
146
+ [fn_name, "->(*args) { ::#{to_qualified(ref.module_name)}::Internal.#{ref.name}(*args) }"]
147
+ }.to_h
148
+
149
+ return "" if fn_map.empty?
150
+
151
+ fn_map_str = Pretty.hash(fn_map)
152
+
153
+ ruby_classes
154
+ .map { "Jade::Runtime.register_impl(#{iface_qname.inspect}, #{it}, #{fn_map_str})" }
155
+ .join(Pretty.newline)
156
+ end
157
+
158
+ def generate_function(impl_fn, registry, interface, type_name)
159
+ impl_fn => AST::ImplementationFunction(name: fn_name, fn:)
160
+
161
+ case fn
162
+ in AST::Lambda(params:, body:)
163
+ synth = impl_synthetic_name(interface, type_name, fn_name)
164
+ param_str = params.map { generate_node(it, registry) }.join(', ')
165
+ sig = param_str.empty? ? '' : "(#{param_str})"
166
+
167
+ Pretty.block("def #{synth}#{sig}", generate_node(body, registry))
168
+
169
+ # Bare VariableReference, and the auto-invoke FunctionCall the
170
+ # desugar pass synthesises for zero-arg fn refs, both dispatch via
171
+ # impl_fn_ref — no method emitted here.
172
+ in AST::VariableReference | AST::FunctionCall
173
+ nil
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,89 @@
1
+ module Jade
2
+ module Codegen
3
+ module Inline
4
+ extend self
5
+ extend Helpers
6
+
7
+ def try_for(callee, args, dictionaries, registry)
8
+ try_sort_by_block(callee, args, dictionaries, registry)
9
+ .then { return it if it }
10
+
11
+ try_block_form(callee, args, registry)
12
+ .then { return it if it }
13
+
14
+ fn = resolve_inline_fn(callee, dictionaries, registry)
15
+ return nil unless fn && fn.arity == args.size
16
+
17
+ fn.call(*args.map { generate_node(it, registry) })
18
+ end
19
+
20
+ private
21
+
22
+ # Native block form is 2-3× faster than `&lambda` because Ruby skips
23
+ # the lambda-to-block conversion per call.
24
+ def try_block_form(callee, args, registry)
25
+ return nil unless args.last in AST::Lambda(params: lambda_params, body: lambda_body)
26
+ return nil unless simple_lambda_params?(lambda_params)
27
+ return nil unless resolve_callee_symbol(callee, registry) in Symbol::StdlibFunction(module_name:, name:)
28
+
29
+ template = Inlines.block_for("#{module_name}.#{name}")
30
+ return nil unless template
31
+
32
+ template.call(
33
+ *args[0...-1].map { generate_node(it, registry) },
34
+ lambda_params.map { lambda_param_name(it) }.join(', '),
35
+ generate_node(lambda_body, registry),
36
+ )
37
+ end
38
+
39
+ def try_sort_by_block(callee, args, dictionaries, registry)
40
+ return nil unless resolve_callee_symbol(callee, registry) in Symbol::StdlibFunction(module_name: 'List', name: 'sort_by')
41
+ return nil unless args.last in AST::Lambda(params: lambda_params, body: lambda_body)
42
+ return nil unless simple_lambda_params?(lambda_params)
43
+ return nil unless dictionaries&.first.is_a?(Symbol::Implementation)
44
+ return nil unless Inlines.native_compare?(dictionaries.first, registry)
45
+
46
+ xs = generate_node(args[0], registry)
47
+ params = lambda_params.map { lambda_param_name(it) }.join(', ')
48
+ body = generate_node(lambda_body, registry)
49
+
50
+ "#{xs}.sort_by { |#{params}| #{body} }"
51
+ end
52
+
53
+ def resolve_inline_fn(callee, dictionaries, registry)
54
+ case resolve_callee_symbol(callee, registry)
55
+ in Symbol::StdlibFunction(module_name:, name:)
56
+ qualified = "#{module_name}.#{name}"
57
+ Inlines.for(qualified) ||
58
+ Inlines.comparison_for(qualified, dictionaries, registry) ||
59
+ Inlines.neq_for(qualified, dictionaries, registry) ||
60
+ Inlines.derived_for(qualified, dictionaries, registry)
61
+
62
+ in Symbol::InterfaceFunction(name: fn_name) if dictionaries&.first.is_a?(Symbol::Implementation)
63
+ interface_impl_inline(dictionaries.first.functions[fn_name], registry)
64
+
65
+ else
66
+ nil
67
+ end
68
+ end
69
+
70
+ def interface_impl_inline(entry, registry)
71
+ return nil unless entry in Symbol::ValueRef
72
+ return nil unless registry.lookup(entry) in Symbol::StdlibFunction(module_name:, name:)
73
+
74
+ Inlines.for("#{module_name}.#{name}")
75
+ end
76
+
77
+ def simple_lambda_params?(params)
78
+ params.all? { it.is_a?(AST::Pattern::Binding) || it.is_a?(AST::Pattern::Wildcard) }
79
+ end
80
+
81
+ def lambda_param_name(pattern)
82
+ case pattern
83
+ in AST::Pattern::Binding(name:) then name
84
+ in AST::Pattern::Wildcard then '_'
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,326 @@
1
+ module Jade
2
+ module Codegen
3
+ module Inlines
4
+ extend self
5
+
6
+ # Ruby's native ordering on these types matches LT/EQ/GT semantics, so
7
+ # the derived comparison ops can collapse to plain operators.
8
+ RUBY_NATIVE_COMPARES = %w[Basics.int_compare Basics.float_compare String.str_compare].to_set.freeze
9
+ RUBY_NATIVE_EQS = %w[Basics.int_eq Basics.float_eq Basics.bool_eq String.str_eq].to_set.freeze
10
+
11
+ DERIVED_COMPARISONS = {
12
+ 'Basics.(<)' => ->(a, b) { "(#{a} < #{b})" },
13
+ 'Basics.(>)' => ->(a, b) { "(#{a} > #{b})" },
14
+ 'Basics.(<=)' => ->(a, b) { "(#{a} <= #{b})" },
15
+ 'Basics.(>=)' => ->(a, b) { "(#{a} >= #{b})" },
16
+ }.freeze
17
+
18
+ INLINES = {
19
+ 'Basics.identity' => ->(a) { a },
20
+ 'Basics.always' => ->(x) { "->(_) { #{x} }" },
21
+ 'Basics.int_add' => ->(a, b) { "(#{a} + #{b})" },
22
+ 'Basics.int_sub' => ->(a, b) { "(#{a} - #{b})" },
23
+ 'Basics.int_mul' => ->(a, b) { "(#{a} * #{b})" },
24
+ 'Basics.int_div' => ->(a, b) { "(#{a} / #{b})" },
25
+ 'Basics.mod' => ->(a, b) { "(#{a} % #{b})" },
26
+ 'Basics.float_add' => ->(a, b) { "(#{a} + #{b})" },
27
+ 'Basics.float_sub' => ->(a, b) { "(#{a} - #{b})" },
28
+ 'Basics.float_mul' => ->(a, b) { "(#{a} * #{b})" },
29
+ 'Basics.float_div' => ->(a, b) { "(#{a} / #{b})" },
30
+ 'Basics.int_compare' => ->(a, b) { "#{a}.compare(#{b})" },
31
+ 'Basics.float_compare' => ->(a, b) { "#{a}.compare(#{b})" },
32
+ 'Basics.(&&)' => ->(a, b) { "(#{a} && #{b})" },
33
+ 'Basics.(||)' => ->(a, b) { "(#{a} || #{b})" },
34
+ 'Basics.not' => ->(a) { "(!#{a})" },
35
+ 'Basics.int_eq' => ->(a, b) { "(#{a} == #{b})" },
36
+ 'Basics.float_eq' => ->(a, b) { "(#{a} == #{b})" },
37
+ 'Basics.bool_eq' => ->(a, b) { "(#{a} == #{b})" },
38
+ 'Basics.to_float' => ->(n) { "#{n}.to_f" },
39
+ 'Basics.floor' => ->(n) { "#{n}.floor" },
40
+ 'Basics.ceiling' => ->(n) { "#{n}.ceil" },
41
+ 'Basics.round' => ->(n) { "#{n}.round" },
42
+ 'Basics.truncate' => ->(n) { "#{n}.truncate" },
43
+
44
+ 'String.str_append' => ->(a, b) { "(#{a} + #{b})" },
45
+ 'String.str_eq' => ->(a, b) { "(#{a} == #{b})" },
46
+ 'String.str_compare' => ->(a, b) { "#{a}.compare(#{b})" },
47
+ 'String.empty?' => ->(s) { "#{s}.empty?" },
48
+ 'String.length' => ->(s) { "#{s}.length" },
49
+ 'String.reverse' => ->(s) { "#{s}.reverse" },
50
+ 'String.cons' => ->(head, tail) { "(#{head} + #{tail})" },
51
+ 'String.from_char' => ->(c) { c },
52
+ 'String.from_int' => ->(n) { "#{n}.to_s" },
53
+ 'String.to_int' => ->(s) { "(Jade::Maybe::Just[Integer(#{s}, 10)] rescue Jade::Maybe::Nothing[])" },
54
+ 'String.uncons' => ->(s) { "#{s}.then { |s| s.empty? ? Jade::Maybe::Nothing[] : Jade::Maybe::Just[Jade::Tuple::Tuple2[s[0], s[1..]]] }" },
55
+ 'String.repeat' => ->(s, n) { "(#{s} * #{n})" },
56
+ 'String.split' => ->(s, by) { "#{s}.split(#{by})" },
57
+ 'String.indexes' => ->(s, sub) { "->(s, sub) { next [] if sub.empty?; found = []; offset = 0; while (hit = s.index(sub, offset)); found << hit; offset = hit + sub.length; end; found }.(#{s}, #{sub})" },
58
+ 'String.concat' => ->(xs) { "#{xs}.join" },
59
+ 'String.join' => ->(xs, with) { "#{xs}.join(#{with})" },
60
+ 'String.map' => ->(s, fn) { "#{s}.chars.map(&#{fn}).join" },
61
+ 'String.trim' => ->(s) { "#{s}.strip" },
62
+ 'String.trim_left' => ->(s) { "#{s}.lstrip" },
63
+ 'String.trim_right' => ->(s) { "#{s}.rstrip" },
64
+ 'String.to_lower' => ->(s) { "#{s}.downcase" },
65
+ 'String.to_upper' => ->(s) { "#{s}.upcase" },
66
+ 'String.contains?' => ->(s, sub) { "#{s}.include?(#{sub})" },
67
+ 'String.starts_with?' => ->(s, p) { "#{s}.start_with?(#{p})" },
68
+ 'String.ends_with?' => ->(s, p) { "#{s}.end_with?(#{p})" },
69
+ 'String.replace' => ->(s, t, r) { "#{s}.gsub(#{t}, #{r})" },
70
+ 'String.slice' => ->(s, a, b) { "(#{s}[#{a}...#{b}] || '')" },
71
+ 'String.words' => ->(s) { "#{s}.split(/\\s+/).reject(&:empty?)" },
72
+ 'String.lines' => ->(s) { "#{s}.split(\"\\n\", -1)" },
73
+ 'String.to_list' => ->(s) { "#{s}.chars" },
74
+ 'String.from_list' => ->(xs) { "#{xs}.join" },
75
+
76
+ 'List.head' => ->(xs) { "#{xs}.then { |xs| xs.empty? ? Jade::Maybe::Nothing[] : Jade::Maybe::Just[xs.first] }" },
77
+ 'List.find' => ->(xs, fn) { "#{xs}.find(&#{fn}).then { |m| m.nil? ? Jade::Maybe::Nothing[] : Jade::Maybe::Just[m] }" },
78
+ 'List.partition' => ->(xs, fn) { "Jade::Tuple::Tuple2[*#{xs}.partition(&#{fn})]" },
79
+ 'List.unzip' => ->(xs) { "#{xs}.then { |xs| Jade::Tuple::Tuple2[xs.map(&:_1), xs.map(&:_2)] }" },
80
+ 'List.singleton' => ->(x) { "[#{x}]" },
81
+ 'List.repeat' => ->(x, n) { "([#{x}] * #{n})" },
82
+ 'List.range' => ->(lo, hi) { "(#{lo}..#{hi}).to_a" },
83
+ 'List.empty?' => ->(xs) { "#{xs}.empty?" },
84
+ 'List.length' => ->(xs) { "#{xs}.length" },
85
+ 'List.reverse' => ->(xs) { "#{xs}.reverse" },
86
+ 'List.tail' => ->(xs) { "#{xs}.drop(1)" },
87
+ 'List.map' => ->(xs, fn) { "#{xs}.map(&#{fn})" },
88
+ 'List.and_then' => ->(xs, fn) { "#{xs}.flat_map(&#{fn})" },
89
+ 'List.indexed_map' => ->(xs, fn) { "#{xs}.each_with_index.map { |x, i| (#{fn}).(i, x) }" },
90
+ 'List.fold' => ->(xs, init, fn) { "#{xs}.reduce(#{init}, &#{fn})" },
91
+ 'List.filter' => ->(xs, fn) { "#{xs}.filter(&#{fn})" },
92
+ 'List.list_append' => ->(a, b) { "(#{a} + #{b})" },
93
+ 'List.any?' => ->(xs, fn) { "#{xs}.any?(&#{fn})" },
94
+ 'List.all?' => ->(xs, fn) { "#{xs}.all?(&#{fn})" },
95
+ 'List.take' => ->(xs, n) { "#{xs}.first([#{n}, 0].max)" },
96
+ 'List.drop' => ->(xs, n) { "#{xs}.drop([#{n}, 0].max)" },
97
+ 'List.concat' => ->(xs) { "#{xs}.flatten(1)" },
98
+
99
+ 'Char.to_code' => ->(c) { "#{c}.ord" },
100
+ 'Char.from_code' => ->(code) { "(Jade::Maybe::Just[#{code}.chr] rescue Jade::Maybe::Nothing[])" },
101
+ 'Char.digit?' => ->(c) { "#{c}.match?(/\\d/)" },
102
+ 'Char.alpha?' => ->(c) { "#{c}.match?(/[a-zA-Z]/)" },
103
+ 'Char.alpha_numeric?' => ->(c) { "#{c}.match?(/[a-zA-Z0-9]/)" },
104
+ 'Char.upper?' => ->(c) { "#{c}.match?(/[A-Z]/)" },
105
+ 'Char.lower?' => ->(c) { "#{c}.match?(/[a-z]/)" },
106
+ 'Char.char_eq' => ->(a, b) { "(#{a} == #{b})" },
107
+
108
+ 'Tuple.pair' => ->(a, b) { "Jade::Tuple::Tuple2[#{a}, #{b}]" },
109
+ 'Tuple.first' => ->(t) { "#{t}._1" },
110
+ 'Tuple.second' => ->(t) { "#{t}._2" },
111
+
112
+ 'Bytes.empty' => ->() { "Jade::Bytes::Bytes[String.new(encoding: Encoding::BINARY)]" },
113
+ 'Bytes.width' => ->(b) { "#{b}.bin.bytesize" },
114
+ 'Bytes.to_list' => ->(b) { "#{b}.bin.bytes" },
115
+ 'Bytes.from_string' => ->(s) { "Jade::Bytes::Bytes[#{s}.b]" },
116
+ 'Bytes.to_string' => ->(b) { "#{b}.bin.dup.force_encoding(Encoding::UTF_8).then { it.valid_encoding? ? Jade::Maybe::Just[it] : Jade::Maybe::Nothing[] }" },
117
+ 'Bytes.bytes_eq' => ->(a, b) { "(#{a}.bin == #{b}.bin)" },
118
+ 'Bytes.bytes_append' => ->(a, b) { "Jade::Bytes::Bytes[#{a}.bin + #{b}.bin]" },
119
+ 'Bytes.to_hex' => ->(b) { "#{b}.bin.unpack1('H*')" },
120
+ 'Bytes.to_base64_url' => ->(b) { "::Base64.urlsafe_encode64(#{b}.bin, padding: false)" },
121
+
122
+ 'Dict.empty' => ->() { "Jade::Dict::Dict[{}]" },
123
+ 'Dict.singleton' => ->(k, v) { "Jade::Dict::Dict[{ #{k} => #{v} }]" },
124
+ 'Dict.get' => ->(d, k) { "#{d}.hash.then { |h| #{k}.then { |k| h.key?(k) ? Jade::Maybe::Just[h[k]] : Jade::Maybe::Nothing[] } }" },
125
+ 'Dict.empty?' => ->(d) { "#{d}.hash.empty?" },
126
+ 'Dict.size' => ->(d) { "#{d}.hash.size" },
127
+ 'Dict.member?' => ->(d, k) { "#{d}.hash.key?(#{k})" },
128
+ 'Dict.insert' => ->(d, k, v) { "Jade::Dict::Dict[#{d}.hash.merge(#{k} => #{v})]" },
129
+ 'Dict.remove' => ->(d, k) { "Jade::Dict::Dict[#{d}.hash.except(#{k})]" },
130
+ 'Dict.keys' => ->(d) { "#{d}.hash.keys" },
131
+ 'Dict.values' => ->(d) { "#{d}.hash.values" },
132
+ 'Dict.to_list' => ->(d) { "#{d}.hash.map { |k, v| Jade::Tuple::Tuple2[k, v] }" },
133
+ 'Dict.from_list' => ->(pairs) { "Jade::Dict::Dict[#{pairs}.each_with_object({}) { |p, h| h[p._1] = p._2 }]" },
134
+ 'Dict.union' => ->(l, r) { "Jade::Dict::Dict[#{r}.hash.merge(#{l}.hash)]" },
135
+ 'Dict.dict_eq' => ->(a, b) { "(#{a}.hash == #{b}.hash)" },
136
+
137
+ 'Set.empty' => ->() { "Jade::Set::Set[{}]" },
138
+ 'Set.singleton' => ->(v) { "Jade::Set::Set[{ #{v} => true }]" },
139
+ 'Set.empty?' => ->(s) { "#{s}.hash.empty?" },
140
+ 'Set.size' => ->(s) { "#{s}.hash.size" },
141
+ 'Set.member?' => ->(s, v) { "#{s}.hash.key?(#{v})" },
142
+ 'Set.insert' => ->(s, v) { "Jade::Set::Set[#{s}.hash.merge(#{v} => true)]" },
143
+ 'Set.remove' => ->(s, v) { "Jade::Set::Set[#{s}.hash.except(#{v})]" },
144
+ 'Set.to_list' => ->(s) { "#{s}.hash.keys" },
145
+ 'Set.from_list' => ->(xs) { "Jade::Set::Set[#{xs}.each_with_object({}) { |v, h| h[v] = true }]" },
146
+ 'Set.union' => ->(l, r) { "Jade::Set::Set[#{l}.hash.merge(#{r}.hash)]" },
147
+ 'Set.set_eq' => ->(a, b) { "(#{a}.hash == #{b}.hash)" },
148
+ }.freeze
149
+
150
+ # Native block form is 2-3× faster than `&lambda` — Ruby skips
151
+ # lambda-to-block conversion per call.
152
+ BLOCK_INLINES = {
153
+ 'List.map' => ->(xs, params, body) { "#{xs}.map { |#{params}| #{body} }" },
154
+ 'List.filter' => ->(xs, params, body) { "#{xs}.filter { |#{params}| #{body} }" },
155
+ 'List.fold' => ->(xs, init, params, body) { "#{xs}.reduce(#{init}) { |#{params}| #{body} }" },
156
+ 'List.and_then' => ->(xs, params, body) { "#{xs}.flat_map { |#{params}| #{body} }" },
157
+ 'List.indexed_map' => ->(xs, params, body) { "#{xs}.each_with_index.map { |#{params.split(', ').reverse.join(', ')}| #{body} }" },
158
+ 'List.any?' => ->(xs, params, body) { "#{xs}.any? { |#{params}| #{body} }" },
159
+ 'List.all?' => ->(xs, params, body) { "#{xs}.all? { |#{params}| #{body} }" },
160
+ 'List.find' => ->(xs, params, body) { "#{xs}.find { |#{params}| #{body} }.then { |m| m.nil? ? Jade::Maybe::Nothing[] : Jade::Maybe::Just[m] }" },
161
+ 'List.partition' => ->(xs, params, body) { "Jade::Tuple::Tuple2[*#{xs}.partition { |#{params}| #{body} }]" },
162
+ 'String.map' => ->(s, params, body) { "#{s}.chars.map { |#{params}| #{body} }.join" },
163
+ }.freeze
164
+
165
+ # Bodies that don't fit a single Ruby expression.
166
+ NO_INLINE = %w[
167
+ List.sort_with
168
+ List.sort_by_with
169
+ List.filter_map
170
+ List.zip
171
+ List.member_with
172
+ List.maximum_with
173
+ List.minimum_with
174
+ Decode.and_map
175
+ Decode.and_then
176
+ Decode.bool
177
+ Decode.decode
178
+ Decode.decode_string
179
+ Decode.dict
180
+ Decode.fail
181
+ Decode.field
182
+ Decode.float
183
+ Decode.from_result
184
+ Decode.index
185
+ Decode.int
186
+ Decode.list
187
+ Decode.map
188
+ Decode.nullable
189
+ Decode.one_of
190
+ Decode.optional
191
+ Decode.optional_field
192
+ Decode.required
193
+ Decode.sequence
194
+ Decode.string
195
+ Decode.succeed
196
+ Decode.tuple
197
+ Decode.tuple3
198
+ Decode.tuple4
199
+ Decode.type_
200
+ Decode.variant
201
+ Encode.bool
202
+ Encode.dict
203
+ Encode.encode_to_string
204
+ Encode.field
205
+ Encode.float
206
+ Encode.int
207
+ Encode.list
208
+ Encode.null
209
+ Encode.nullable
210
+ Encode.object
211
+ Encode.string
212
+ Encode.tuple
213
+ Encode.tuple3
214
+ Encode.tuple4
215
+ Encode.variant
216
+ Task.and_then
217
+ Task.fail
218
+ Task.from_result
219
+ Task.map
220
+ Task.map_error
221
+ Task.on_error
222
+ Task.run
223
+ Task.sequence
224
+ Task.succeed
225
+ Bytes.from_list
226
+ Bytes.from_hex
227
+ Bytes.from_base64_url
228
+ Dict.update
229
+ Dict.map
230
+ Dict.filter
231
+ Dict.fold
232
+ Dict.merge
233
+ Set.map
234
+ Set.filter
235
+ Set.fold
236
+ Set.intersect
237
+ Set.diff
238
+ ].to_set.freeze
239
+
240
+ def for(qualified_name)
241
+ INLINES[qualified_name]
242
+ end
243
+
244
+ def expected_to_skip?(qualified_name)
245
+ NO_INLINE.include?(qualified_name)
246
+ end
247
+
248
+ def block_for(qualified_name)
249
+ BLOCK_INLINES[qualified_name]
250
+ end
251
+
252
+ def comparison_for(qualified_name, dictionaries, registry)
253
+ template = DERIVED_COMPARISONS[qualified_name]
254
+ return nil unless template
255
+ return nil unless dictionaries&.first.is_a?(Symbol::Implementation)
256
+
257
+ dictionaries.first.functions['compare'].then do |entry|
258
+ next nil unless entry.is_a?(Symbol::ValueRef)
259
+
260
+ fn = registry.lookup(entry)
261
+ next nil unless fn.is_a?(Symbol::StdlibFunction)
262
+ next nil unless RUBY_NATIVE_COMPARES.include?("#{fn.module_name}.#{fn.name}")
263
+
264
+ template
265
+ end
266
+ end
267
+
268
+ def neq_for(qualified_name, dictionaries, registry)
269
+ return nil unless qualified_name == 'Basics.(!=)'
270
+ return nil unless dictionaries&.first.is_a?(Symbol::Implementation)
271
+
272
+ dictionaries.first.functions['(==)'].then do |entry|
273
+ next nil unless entry.is_a?(Symbol::ValueRef)
274
+
275
+ fn = registry.lookup(entry)
276
+ next nil unless fn.is_a?(Symbol::StdlibFunction)
277
+ next nil unless RUBY_NATIVE_EQS.include?("#{fn.module_name}.#{fn.name}")
278
+
279
+ ->(a, b) { "(#{a} != #{b})" }
280
+ end
281
+ end
282
+
283
+ def derived_for(qualified_name, dictionaries, registry)
284
+ return nil unless dictionaries&.first in Symbol::Implementation => impl
285
+
286
+ case qualified_name
287
+ in 'List.sort' if native_compare?(impl, registry)
288
+ ->(xs) { "#{xs}.sort" }
289
+
290
+ in 'List.sort_by' if native_compare?(impl, registry)
291
+ ->(xs, key) { "#{xs}.sort_by(&#{key})" }
292
+
293
+ in 'List.maximum' if native_compare?(impl, registry)
294
+ ->(xs) { "#{xs}.max.then { |m| m.nil? ? Jade::Maybe::Nothing[] : Jade::Maybe::Just[m] }" }
295
+
296
+ in 'List.minimum' if native_compare?(impl, registry)
297
+ ->(xs) { "#{xs}.min.then { |m| m.nil? ? Jade::Maybe::Nothing[] : Jade::Maybe::Just[m] }" }
298
+
299
+ in 'List.member?' if native_eq?(impl, registry)
300
+ ->(xs, e) { "#{xs}.include?(#{e})" }
301
+
302
+ else
303
+ nil
304
+ end
305
+ end
306
+
307
+ def native_compare?(impl, registry)
308
+ native_impl_fn?(impl, 'compare', RUBY_NATIVE_COMPARES, registry)
309
+ end
310
+
311
+ def native_eq?(impl, registry)
312
+ native_impl_fn?(impl, '(==)', RUBY_NATIVE_EQS, registry)
313
+ end
314
+
315
+ def native_impl_fn?(impl, fn_name, native_set, registry)
316
+ entry = impl.functions[fn_name]
317
+ return false unless entry.is_a?(Symbol::ValueRef)
318
+
319
+ fn = registry.lookup(entry)
320
+ return false unless fn.is_a?(Symbol::StdlibFunction)
321
+
322
+ native_set.include?("#{fn.module_name}.#{fn.name}")
323
+ end
324
+ end
325
+ end
326
+ end
@@ -0,0 +1,54 @@
1
+ module Jade
2
+ module Codegen
3
+ module MethodNames
4
+ extend self
5
+
6
+ INTERFACE_METHODS = {
7
+ 'Basics.Eq' => {
8
+ '(==)' => '==',
9
+ },
10
+ 'Basics.Comparable' => {
11
+ 'compare' => 'compare',
12
+ },
13
+ 'Basics.Numeric' => {
14
+ '(+)' => '+',
15
+ '(-)' => '-',
16
+ '(*)' => '*',
17
+ '(/)' => '/',
18
+ },
19
+ 'Basics.Appendable' => {
20
+ '(++)' => '+',
21
+ },
22
+ }.freeze
23
+
24
+ OPERATOR_INTERFACES = INTERFACE_METHODS.keys.to_set.freeze
25
+
26
+ CALL_OPERATORS = {
27
+ 'Basics.(==)' => '==',
28
+ 'Basics.(!=)' => '!=',
29
+ 'Basics.(<)' => '<',
30
+ 'Basics.(>)' => '>',
31
+ 'Basics.(<=)' => '<=',
32
+ 'Basics.(>=)' => '>=',
33
+ 'Basics.(+)' => '+',
34
+ 'Basics.(-)' => '-',
35
+ 'Basics.(*)' => '*',
36
+ 'Basics.(/)' => '/',
37
+ 'Basics.(++)' => '+',
38
+ 'Basics.compare' => 'compare',
39
+ }.freeze
40
+
41
+ def interface_method(interface_qname, fn_name)
42
+ INTERFACE_METHODS.dig(interface_qname, fn_name)
43
+ end
44
+
45
+ def operator_interface?(interface_qname)
46
+ OPERATOR_INTERFACES.include?(interface_qname)
47
+ end
48
+
49
+ def call_operator(qualified_name)
50
+ CALL_OPERATORS[qualified_name]
51
+ end
52
+ end
53
+ end
54
+ end