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,367 @@
1
+ module Jade
2
+ module Codegen
3
+ module FunctionCall
4
+ extend self
5
+ extend Helpers
6
+
7
+ def generate(node, registry)
8
+ node => AST::FunctionCall(callee:, args:, dictionaries:)
9
+
10
+ variant_sym = keyed_variant_constructor(callee, registry)
11
+ return generate_keyed_variant_call(variant_sym, args, registry) if variant_sym
12
+
13
+ try_operator_call(callee, args, registry)
14
+ .then { return it if it }
15
+
16
+ Inline.try_for(callee, args, dictionaries, registry)
17
+ .then { return it if it }
18
+
19
+ return constructor_call(callee, args, registry) if constructor_callee?(callee, registry)
20
+
21
+ [generate_many(args, registry), generate_dict_args(callee, dictionaries, registry)]
22
+ .reject(&:empty?)
23
+ .join(', ')
24
+ .then { "#{generate_callee(callee, args, registry, dictionaries)}#{invocation_op(callee, registry)}(#{it})" }
25
+ end
26
+
27
+ def constructor_call(callee, args, registry)
28
+ resolve_callee_symbol(callee, registry)
29
+ .then { to_qualified(it.qualified_name) }
30
+ .then { "#{it}[#{generate_many(args, registry)}]" }
31
+ end
32
+
33
+ def constructor_callee?(callee, registry)
34
+ return true if callee.is_a?(AST::ConstructorReference)
35
+
36
+ resolve_callee_symbol(callee, registry).is_a?(Symbol::Constructor)
37
+ end
38
+
39
+ # Direct-def call sites (`Foo::Internal.name(args)`) for plain user fns;
40
+ # `.call(args)` for everything else (lambdas, Procs, Methods).
41
+ def invocation_op(callee, registry)
42
+ case callee.symbol
43
+ in Symbol::ValueRef => ref then invocation_op_for(registry.lookup(ref))
44
+ in symbol then invocation_op_for(symbol)
45
+ end
46
+ end
47
+
48
+ def invocation_op_for(symbol)
49
+ symbol.is_a?(Symbol::Function) ? '' : '.call'
50
+ end
51
+
52
+ def try_operator_call(callee, args, registry)
53
+ MethodNames
54
+ .call_operator(callee_qname(callee, registry))
55
+ &.then { |op| emit_operator(op, args, registry) }
56
+ end
57
+
58
+ def callee_qname(callee, registry)
59
+ case resolve_callee_symbol(callee, registry)
60
+ in Symbol::InterfaceFunction | Symbol::StdlibFunction => sym then sym.qualified_name
61
+ else nil
62
+ end
63
+ end
64
+
65
+ def emit_operator(op, args, registry)
66
+ args
67
+ .map { generate_node(it, registry) }
68
+ .then { |(a, b)| op == 'compare' ? "#{a}.compare(#{b})" : "(#{a} #{op} #{b})" }
69
+ end
70
+
71
+ def generate_impl_dispatch(impl, registry)
72
+ impl
73
+ .deps
74
+ .map { |dep| dispatch_for_dep(dep, registry) }
75
+ .then do |dep_dispatches|
76
+ impl.functions.transform_values do |fn|
77
+ generate_impl_fn(fn, dep_dispatches, impl.functions, registry)
78
+ end
79
+ end
80
+ end
81
+
82
+ # An Implementation's dep is one of two dictionary-slot shapes: a
83
+ # concrete Implementation (recurse to a `{fn_name => ruby_code}` hash)
84
+ # or a Type::Constraint(Var) marker for a free-var dep that's late-bound
85
+ # from the caller's dict env (emit the raw dict reference as a String —
86
+ # build_impl_arg threads it into the impl_arg slot without re-wrapping).
87
+ def dispatch_for_dep(dep, registry)
88
+ case dep
89
+ in Symbol::Implementation
90
+ generate_impl_dispatch(dep, registry)
91
+
92
+ in Type::Constraint(type: Type::Var)
93
+ dispatch_value(dep, registry)
94
+ end
95
+ end
96
+
97
+ def dispatch_value(entry, registry)
98
+ case entry
99
+ in Type::Constraint(interface:, type: Type::Var(id:))
100
+ Codegen.dict_env[[interface, id]]
101
+
102
+ in Symbol::Implementation
103
+ Pretty.hash(generate_impl_dispatch(entry, registry))
104
+ end
105
+ end
106
+
107
+ # Polymorphic fn referenced as a value (not called). Wraps the fn with
108
+ # its dispatched dictionaries so the result is a monomorphic callable
109
+ # matching the type at the use site. Returns nil when the symbol
110
+ # doesn't need wrapping; the caller falls back to its default
111
+ # reference emission.
112
+ def reference_with_dictionaries(symbol, dictionaries, registry)
113
+ return nil if dictionaries.empty?
114
+
115
+ case symbol
116
+ in Symbol::StdlibFunction => fn if fn.constraints.any?
117
+ dictionaries
118
+ .map { dispatch_dict(it, registry) }
119
+ .then { generate_impl_fn(fn.codegen, it, {}, registry) }
120
+
121
+ in Symbol::Function => fn if dict_constraints(fn, registry).any?
122
+ param_names = fn.params.size.times.map { param_synthetic_name(it) }
123
+
124
+ fn_constraints(fn, registry)
125
+ .each_with_index
126
+ .filter_map { |c, i| dispatch_value(dictionaries[i], registry) if c.type.is_a?(Type::Var) }
127
+ .then { (param_names + it).join(', ') }
128
+ .then { "#{to_qualified(fn.module_name)}::Internal.#{fn_target_name(fn, registry)}(#{it})" }
129
+ .then { Pretty.lambda(param_names.join(', '), it) }
130
+
131
+ in Symbol::InterfaceFunction => fn
132
+ dispatch_value(dictionaries.first, registry)
133
+ &.then { "#{it}[#{fn.name.inspect}]" } ||
134
+ fail("no dict in scope to reference interface method `#{fn.qualified_name}` as a value")
135
+
136
+ else
137
+ nil
138
+ end
139
+ end
140
+
141
+ private
142
+
143
+ def keyed_variant_constructor(callee, registry)
144
+ case resolve_callee_symbol(callee, registry)
145
+ in Symbol::Constructor(args: [Symbol::RecordType]) => resolved
146
+ resolved
147
+
148
+ else
149
+ nil
150
+ end
151
+ end
152
+
153
+ def generate_keyed_variant_call(constructor, args, registry)
154
+ qualified = to_qualified(constructor.qualified_name)
155
+ record_fields = constructor.args[0].fields.keys
156
+
157
+ args[0] => arg
158
+ case arg
159
+ in AST::RecordLiteral(fields:)
160
+ fields_by_key = fields.to_h { [it.key, it.value] }
161
+ record_fields
162
+ .map { generate_node(fields_by_key.fetch(it), registry) }
163
+ .join(', ')
164
+ .then { "#{qualified}[#{it}]" }
165
+ else
166
+ "#{qualified}[**#{generate_node(arg, registry)}.to_h]"
167
+ end
168
+ end
169
+
170
+
171
+ def generate_callee(callee, args, registry, dictionaries)
172
+ return generate_node(callee, registry) if callee in AST::RecordAccess | AST::FunctionCall | AST::Grouping
173
+
174
+ case callee.symbol
175
+ in Symbol::ValueRef
176
+ registry
177
+ .lookup(callee.symbol)
178
+ .then { generate_callee(callee.with(symbol: it), args, registry, dictionaries) }
179
+
180
+ in Symbol::InteropFunction
181
+ registry
182
+ .lookup(callee.symbol.to_ref)
183
+ .then { PortDecoder.task_call(it, registry, dictionaries) }
184
+
185
+ in Symbol::StdlibFunction => symbol if symbol.constraints.any?
186
+ dictionaries
187
+ .map { |entry| dispatch_dict(entry, registry) }
188
+ .then { generate_impl_fn(symbol.codegen, it, {}, registry) }
189
+
190
+ in Symbol::StdlibFunction
191
+ callee.symbol.codegen
192
+
193
+ in Symbol::Variable(name:)
194
+ name
195
+
196
+ in Symbol::Lambda
197
+ generate_node(callee, registry)
198
+
199
+ in Symbol::Function => fn_sym
200
+ to_qualified(fn_sym.module_name) + "::Internal." + fn_target_name(fn_sym, registry)
201
+
202
+ in Symbol::StdlibImplementation => symbol
203
+ dictionaries
204
+ .reduce({}) { |acc, entry| acc.merge dispatch_dict(entry, registry) }
205
+ .then { generate_stdlib_implementation(symbol, registry, it) }
206
+
207
+ in Symbol::InterfaceFunction => symbol if dictionaries.any?
208
+ dispatch_lookup(dictionaries.first, symbol.name, registry) {
209
+ runtime_dispatch(symbol, args, registry)
210
+ }
211
+
212
+ in Symbol::InterfaceFunction => symbol
213
+ runtime_dispatch(symbol, args, registry)
214
+ end
215
+ end
216
+
217
+ # When a user fn has var-typed constraints, two definitions are emitted:
218
+ # `name` (Ruby-boundary wrapper, no dicts) and `__name__impl__` (takes
219
+ # dicts). Jade-internal calls target the latter.
220
+ def fn_target_name(fn_sym, registry)
221
+ return fn_sym.name if dict_constraints(fn_sym, registry).empty?
222
+
223
+ fn_impl_synthetic_name(fn_sym.name)
224
+ end
225
+
226
+ # Returns the list of dict args to pass after regular args. Only
227
+ # Symbol::Function callees take dict params; other branches dispatch
228
+ # via `dictionaries` directly inside generate_callee. dictionaries are
229
+ # attached in callee constraint order; only var-typed slots need a
230
+ # runtime dict (others are resolved at finalize).
231
+ def generate_dict_args(callee, dictionaries, registry)
232
+ symbol =
233
+ case callee.symbol
234
+ in Symbol::ValueRef then registry.lookup(callee.symbol)
235
+ else callee.symbol
236
+ end
237
+
238
+ return "" unless symbol.is_a?(Symbol::Function)
239
+
240
+ fn_constraints(symbol, registry)
241
+ .each_with_index
242
+ .filter_map { |c, i| dispatch_value(dictionaries[i], registry) if c.type.is_a?(Type::Var) }
243
+ .join(', ')
244
+ end
245
+
246
+ # Ruby-block intrinsics (Dict's `Eq k`, etc.) ignore dispatches — the
247
+ # `in String` body of `generate_impl_fn` drops them.
248
+ def dispatch_dict(entry, registry)
249
+ case entry
250
+ in Symbol::Implementation
251
+ generate_impl_dispatch(entry, registry)
252
+
253
+ in Type::Constraint(interface:, type: Type::Var(id:))
254
+ Codegen.dict_env[[interface, id]] ||
255
+ fail("no dict in scope for #{interface}")
256
+ end
257
+ end
258
+
259
+ # Generates a Ruby expression for `dict[fn_name]` from a dictionary entry.
260
+ # Falls back to the supplied block when a var-typed marker has no entry
261
+ # in the current dict_env (e.g. an anonymous lambda's body).
262
+ def dispatch_lookup(entry, fn_name, registry, &fallback)
263
+ case entry
264
+ in Type::Constraint(interface:, type: Type::Var(id:))
265
+ Codegen
266
+ .dict_env[[interface, id]]
267
+ &.then { "#{it}[#{fn_name.inspect}]" } || fallback.call
268
+
269
+ in Symbol::Implementation
270
+ generate_impl_dispatch(entry, registry)[fn_name]
271
+ end
272
+ end
273
+
274
+ def runtime_dispatch(symbol, args, registry)
275
+ generate_node(args.first, registry)
276
+ .then { "Jade::Runtime.impl_for(#{symbol.interface.qualified_name.inspect}, #{it})[#{symbol.name.inspect}]" }
277
+ end
278
+
279
+ def generate_impl_fn(fn, dep_dispatches, sibling_fns, registry)
280
+ case fn
281
+ in Symbol::DerivedFunction(params:, body:)
282
+ inner = params.empty? \
283
+ ? emit(body, registry)
284
+ : Pretty.lambda(params.join(', '), emit(body, registry))
285
+ return inner if dep_dispatches.empty?
286
+
287
+ Pretty.lambda("impl_arg", inner) + ".call(#{build_impl_arg(dep_dispatches)})"
288
+
289
+ # Impl-dispatch dicts hold evaluated values, not callables —
290
+ # `{ 'decoder' => <Decoder>, 'compare' => <Proc(a, b)> }`.
291
+ # `impl_arg[i]['decoder'].desc` works directly; we never `.call`
292
+ # it. So constant slots like `Decode.int` get invoked at synth
293
+ # time; only multi-arg slots keep their Proc shape.
294
+ #
295
+ # The desugar pass that normally auto-invokes zero-arg refs
296
+ # operates on AST. These dicts are built straight from
297
+ # `Symbol::Implementation.functions` at codegen, no AST in
298
+ # between, so the equivalent invoke happens here instead.
299
+ in Symbol::StdlibFunction if fn.constant?
300
+ "#{fn.codegen}.call()"
301
+
302
+ in Symbol::StdlibFunction
303
+ fn.codegen
304
+
305
+ # Ruby-block intrinsic that declares a constraint for type-system
306
+ # honesty but doesn't consume the dict at runtime (Dict ops use Ruby
307
+ # `==`, etc.). The dispatches are dropped — the block sees only the
308
+ # data args, same as the no-constraint path.
309
+ in String
310
+ fn
311
+
312
+ in Symbol::StdlibImplementation
313
+ sibling_dispatch = sibling_fns
314
+ .reject { |_, sib| sib.is_a?(Symbol::StdlibImplementation) }
315
+ .transform_values { |sib| generate_impl_fn(sib, dep_dispatches, sibling_fns, registry) }
316
+ Pretty.lambda(fn.params.join(', '), build_std_impl_str(fn.body, sibling_dispatch, registry))
317
+
318
+ in Symbol::ValueRef
319
+ registry.lookup(fn).then { generate_impl_fn(it, dep_dispatches, sibling_fns, registry) }
320
+
321
+ # 0-arg fn: Ruby auto-invokes on bare reference. Result is the
322
+ # dispatch-slot value (decoder, encoder template, ...) ready to use.
323
+ in Symbol::Function => fn if fn.constant?
324
+ "#{to_qualified(fn.module_name)}::Internal.#{fn.name}"
325
+
326
+ in Symbol::Function => fn
327
+ "#{to_qualified(fn.module_name)}::Internal.method(:#{fn.name})"
328
+ end
329
+ end
330
+
331
+ def build_impl_arg(dep_dispatches)
332
+ dep_dispatches
333
+ .map { it.is_a?(String) ? it : Pretty.hash(it) }
334
+ .then { Pretty.array(it) }
335
+ end
336
+
337
+ # Stdlib intrinsics implementation language.
338
+
339
+ def generate_stdlib_implementation(symbol, registry, dispatch)
340
+ Pretty.lambda(symbol.params.join(', '), build_std_impl_str(symbol.body, dispatch, registry))
341
+ end
342
+
343
+ def build_std_impl_str(body, dispatch, registry)
344
+ case body
345
+ in [:call, fn, args]
346
+ args.map { build_std_impl_str(it, dispatch, registry) }.join(', ')
347
+ .then { "#{build_std_impl_str(fn, dispatch, registry)}.call(#{it})" }
348
+
349
+ in String
350
+ body
351
+
352
+ in [:impl, impl]
353
+ dispatch[impl]
354
+
355
+ in [:fn, name]
356
+ *mod_parts, fn_name = name.split('.')
357
+ sym = Symbol.value_ref(mod_parts.join('.'), fn_name)
358
+ registry.lookup(sym).then { generate_impl_fn(it, [], {}, registry) }
359
+ end
360
+ end
361
+
362
+ def emit(ir, registry)
363
+ Emitter.emit(ir)
364
+ end
365
+ end
366
+ end
367
+ end
@@ -0,0 +1,199 @@
1
+ module Jade
2
+ module Codegen
3
+ module FunctionDeclaration
4
+ extend self
5
+ extend Helpers
6
+
7
+ def generate_boundary_wrapper(node, registry)
8
+ node => AST::FunctionDeclaration(name:, params:, symbol:)
9
+
10
+ entry = registry.get(symbol.module_name)
11
+ return nil unless entry&.exposed_value(name)
12
+
13
+ if !dict_constraints(symbol, registry).empty?
14
+ return not_exposed_stub(symbol, 'polymorphic — no extractable witness for type variable')
15
+ end
16
+
17
+ fn_type = fn_type_for(symbol, registry)
18
+ unless Codegen::Boundary.eligible?(fn_type, registry)
19
+ return not_exposed_stub(symbol, ineligibility_reason(fn_type, registry))
20
+ end
21
+
22
+ args, return_type = Type.signature(fn_type)
23
+ param_names = params.map(&:name)
24
+
25
+ if task_return?(return_type)
26
+ task_wrapper_pair(name, args, param_names, return_type, registry)
27
+ else
28
+ eligible_wrapper(name, args, param_names, return_type, registry)
29
+ end
30
+ end
31
+
32
+ def generate(node, registry)
33
+ node => AST::FunctionDeclaration(name:, params:, body:, symbol:)
34
+
35
+ var_cs = dict_constraints(symbol, registry)
36
+ param_names = params.map { generate_node(it, registry) }
37
+ dict_params = var_cs.each_index.map { dict_synthetic_name(it) }
38
+
39
+ body_code = build_dict_env(var_cs)
40
+ .then { Codegen.with_dict_env(it) { emit_body(body, symbol, param_names, registry) } }
41
+
42
+ target = var_cs.empty? ? name : fn_impl_synthetic_name(name)
43
+ sig = (param_names + dict_params).join(', ')
44
+ sig_str = sig.empty? ? '' : "(#{sig})"
45
+
46
+ Pretty.block("def #{target}#{sig_str}", body_code)
47
+ end
48
+
49
+ private
50
+
51
+ def emit_body(body, self_sym, param_names, registry)
52
+ if Transforms::TailCall.tail_recursive?(body, self_sym, param_names.size, registry)
53
+ Transforms::TailCall.generate_body(body, registry, self_sym, param_names)
54
+ elsif (shape = Transforms::FoldShape.shape_for(body, self_sym, param_names, registry))
55
+ Transforms::FoldShape.generate_body(shape, registry)
56
+ else
57
+ generate_node(body, registry)
58
+ end
59
+ end
60
+
61
+ def eligible_wrapper(name, args, param_names, return_type, registry)
62
+ decoded_args(args, param_names, registry)
63
+ .then { Pretty.call("Internal.#{name}", it) }
64
+ .then { encode_return(return_type, it, registry) }
65
+ .then { Pretty.block(boundary_def_header(name, param_names), it) }
66
+ end
67
+
68
+ def boundary_def_header(name, param_names)
69
+ "def self.#{name}(#{param_names.join(', ')})"
70
+ end
71
+
72
+ def decoded_args(args, param_names, registry)
73
+ args.zip(param_names).map { |t, pname| decode_call(t, pname, registry) }
74
+ end
75
+
76
+ def decode_call(arg_type, pname, registry)
77
+ Codegen::Boundary::Specialized.decode_expr(arg_type, pname, registry) ||
78
+ Codegen::Boundary::Cache.decoder_for(arg_type, registry)
79
+ .then { "Jade::Interop::Boundary.decode_or_raise(#{it}, #{pname})" }
80
+ end
81
+
82
+ def encode_return(return_type, call_expr, registry)
83
+ if Codegen::Boundary::Specialized.identity_encoder?(return_type)
84
+ call_expr
85
+ elsif (expr = Codegen::Boundary::Specialized.encode_expr(return_type, call_expr, registry))
86
+ expr
87
+ else
88
+ Codegen::Boundary::Cache
89
+ .encoder_for(return_type, registry)
90
+ .then { |encoder| "#{encoder}.call(#{call_expr})" }
91
+ end
92
+ end
93
+
94
+ def task_wrapper_pair(name, args, param_names, task_return, registry)
95
+ ok_enc, err_enc = Codegen::Boundary::Cache.task_arms(task_return, registry)
96
+
97
+ [
98
+ task_run_def(name, args, param_names, ok_enc, err_enc, registry),
99
+ task_bang_def(name, param_names),
100
+ ].join(Pretty.newline(2))
101
+ end
102
+
103
+ def task_run_def(name, args, param_names, ok_enc, err_enc, registry)
104
+ decoded_args(args, param_names, registry)
105
+ .then { Pretty.call("Internal.#{name}", it) }
106
+ .then { task_run_body(it, ok_enc, err_enc) }
107
+ .then { Pretty.block(boundary_def_header(name, param_names), it) }
108
+ end
109
+
110
+ def task_run_body(call_expr, ok_enc, err_enc)
111
+ [
112
+ "case #{call_expr}.run",
113
+ "in Jade::Result::Ok[v] then [\"ok\", #{ok_enc}.call(v)]",
114
+ "in Jade::Result::Err[e] then [\"err\", #{err_enc}.call(e)]",
115
+ 'end',
116
+ ].join(Pretty.newline)
117
+ end
118
+
119
+ def task_bang_def(name, param_names)
120
+ [
121
+ "case #{name}#{paren_or_empty(param_names)}",
122
+ 'in ["ok", v] then v',
123
+ 'in ["err", e] then raise Jade::Interop::TaskError.new(e)',
124
+ 'end',
125
+ ]
126
+ .join(Pretty.newline)
127
+ .then do
128
+ Pretty.block(boundary_def_header("#{name}!", param_names), it)
129
+ end
130
+ end
131
+
132
+ def task_return?(type)
133
+ type in Type::Application(constructor: Type::Constructor(name: 'Task.Task'))
134
+ end
135
+
136
+ def paren_or_empty(items)
137
+ items.empty? ? '' : "(#{items.join(', ')})"
138
+ end
139
+
140
+ def not_exposed_stub(symbol, reason)
141
+ "raise Jade::Interop::NotExposed.new(" \
142
+ "module_name: #{to_qualified(symbol.module_name).inspect}, " \
143
+ "function_name: #{symbol.name.to_sym.inspect}, " \
144
+ "hint: #{reason.inspect})"
145
+ .then { Pretty.block("def self.#{symbol.name}(*)", it) }
146
+ end
147
+
148
+ def ineligibility_reason(fn_type, registry)
149
+ args, ret = Type.signature(fn_type)
150
+
151
+ arg_ineligibility_reason(args, registry) ||
152
+ return_ineligibility_reason(ret, registry) ||
153
+ fail("unreachable: eligible? returned false but no specific reason found for #{fn_type}")
154
+ end
155
+
156
+ def arg_ineligibility_reason(args, registry)
157
+ args.each_with_index do |arg, i|
158
+ if Codegen::Boundary.decoder_for(arg, registry).nil?
159
+ return "argument #{i + 1} of type #{arg} has no Decodable instance"
160
+ end
161
+ end
162
+
163
+ nil
164
+ end
165
+
166
+ def return_ineligibility_reason(ret, registry)
167
+ case ret
168
+ in Type::Application(constructor: Type::Constructor(name: 'Task.Task'), args: [ok_t, err_t])
169
+ if Codegen::Boundary.encoder_for(ok_t, registry).nil?
170
+ "Task ok arm of type #{ok_t} has no Encodable instance"
171
+
172
+ elsif Codegen::Boundary.encoder_for(err_t, registry).nil?
173
+ "Task err arm of type #{err_t} has no Encodable instance"
174
+ end
175
+
176
+ else
177
+ if Codegen::Boundary.encoder_for(ret, registry).nil?
178
+ "return type #{ret} has no Encodable instance"
179
+ end
180
+ end
181
+ end
182
+
183
+ def fn_type_for(symbol, registry)
184
+ registry
185
+ .get(symbol.module_name)
186
+ .env
187
+ .then { it.substitution.apply(it.bindings[symbol.qualified_name].type) }
188
+ end
189
+
190
+ def build_dict_env(var_cs)
191
+ var_cs
192
+ .each_with_index
193
+ .reduce({}) do |env, (c, i)|
194
+ env.merge([c.interface, c.type.id] => dict_synthetic_name(i))
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,103 @@
1
+ module Jade
2
+ module Codegen
3
+ module Helpers
4
+ extend self
5
+
6
+ def generate_many(nodes, registry, sep = ", ")
7
+ nodes.map do
8
+ next yield(it) if block_given?
9
+
10
+ generate_node(it, registry)
11
+ end.join(sep)
12
+ end
13
+
14
+ def to_qualified(module_name)
15
+ base = module_name.gsub('.', '::')
16
+ Stdlib.stdlib_name?(module_name) ? "Jade::#{base}" : base
17
+ end
18
+
19
+ def data_define(fields)
20
+ return "Data.define" if fields.empty?
21
+
22
+ "Data.define(#{fields.map { ":#{it}" }.join(', ')})"
23
+ end
24
+
25
+ def generate_node(node, registry)
26
+ Codegen.generate(node, registry)
27
+ end
28
+
29
+ def resolve_callee_symbol(callee, registry)
30
+ case callee.symbol
31
+ in Symbol::ValueRef => ref then registry.lookup(ref)
32
+ in symbol then symbol
33
+ end
34
+ end
35
+
36
+ def param_synthetic_name(index)
37
+ "__p#{index}__"
38
+ end
39
+
40
+ def impl_synthetic_name(interface, type_name, fn_name)
41
+ sanitized = fn_name.gsub(/[^a-zA-Z0-9_]/) { |c| "x#{c.ord.to_s(16)}" }
42
+ "__impl_#{interface}_#{type_name}_#{sanitized}__"
43
+ end
44
+
45
+ def dict_synthetic_name(index)
46
+ "__dict#{index}__"
47
+ end
48
+
49
+ def fn_impl_synthetic_name(name)
50
+ "__#{name}__impl__"
51
+ end
52
+
53
+ def fn_constraints(fn_symbol, registry)
54
+ env = registry.get(fn_symbol.module_name).env
55
+
56
+ env
57
+ .bindings[fn_symbol.qualified_name]
58
+ .constraints
59
+ .map { env.substitution.apply(it) }
60
+ end
61
+
62
+ # Subset of fn_constraints that need a runtime dict param: those whose
63
+ # type is a bare Type::Var. Other constraints (e.g. Eq(Maybe(α)) where α
64
+ # is unbound but the outer constructor is concrete) are resolved at
65
+ # finalize via the impl table — no dict threaded for them.
66
+ def dict_constraints(fn_symbol, registry)
67
+ fn_constraints(fn_symbol, registry).select { |c| c.type.is_a?(Type::Var) }
68
+ end
69
+
70
+ # Ruby classes that back primitive Jade types. Mirrors stdlib's
71
+ # `native_type` declarations so user impls on primitives register under
72
+ # the right Ruby class. (Lifting this onto Symbol::Union itself is
73
+ # tracked in plans/lift-native-types-into-symbol-table.md.)
74
+ NATIVE_RUBY_CLASSES = {
75
+ 'Basics.Int' => ['Integer'],
76
+ 'Basics.Float' => ['Float'],
77
+ 'Basics.Bool' => ['TrueClass', 'FalseClass'],
78
+ 'String.String' => ['String'],
79
+ 'Char.Char' => ['String'],
80
+ }.freeze
81
+
82
+ # Returns the Ruby class names that values of `type_ref` may have at
83
+ # runtime. Strings here go straight into emitted Ruby. Returns [] for
84
+ # types that have no concrete runtime representation (interfaces).
85
+ def ruby_classes_for_type(type_ref, registry)
86
+ qname = type_ref.qualified_name
87
+ return NATIVE_RUBY_CLASSES[qname] if NATIVE_RUBY_CLASSES.key?(qname)
88
+
89
+ case registry.lookup(type_ref)
90
+ in Symbol::Union(variants:)
91
+ variants.map { "::#{to_qualified(it.qualified_name)}" }
92
+
93
+ in Symbol::Struct
94
+ ["::#{to_qualified(qname)}"]
95
+
96
+ in Symbol::Interface
97
+ []
98
+ end
99
+ end
100
+
101
+ end
102
+ end
103
+ end