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,129 @@
1
+ require 'jade/frontend/type_checking/canonicalize'
2
+ require 'jade/frontend/type_checking/constraints'
3
+ require 'jade/frontend/type_checking/definition'
4
+ require 'jade/frontend/type_checking/env'
5
+ require 'jade/frontend/type_checking/error'
6
+ require 'jade/frontend/type_checking/expected'
7
+ require 'jade/frontend/type_checking/inference'
8
+ require 'jade/frontend/type_checking/loader'
9
+ require 'jade/frontend/type_checking/port_resolution'
10
+ require 'jade/frontend/type_checking/result'
11
+ require 'jade/frontend/type_checking/state'
12
+ require 'jade/frontend/type_checking/substitution'
13
+ require 'jade/frontend/type_checking/unification'
14
+
15
+ require 'jade/frontend/type_checking/generalizer'
16
+
17
+ module Jade
18
+ module Frontend
19
+ module TypeChecking
20
+ extend Inference::Helpers
21
+ extend self
22
+
23
+ def check(entry, registry)
24
+ Loader
25
+ .load(entry, registry)
26
+ .then { check_node(entry.ast, registry, State.init(it, skip_constraints: true), Expected.infer(it.fresh)) }
27
+ .then { Generalizer.generalize(it.first.env) }
28
+ .then { check_node(entry.ast, registry, State.init(it), Expected.infer(it.fresh)) }
29
+ .then { finalize(*it, registry) }
30
+ .map { Canonicalize.run(entry.ast, it, registry) }
31
+ .map { it.canonicalize_node_types }
32
+ .map { entry.with(env: it) }
33
+ .and_then { PortResolution.resolve(it, registry) }
34
+ end
35
+
36
+ def finalize(state, result, registry)
37
+ state.env => { bindings:, entry_name: }
38
+
39
+ errors = bindings
40
+ .select do |k,v|
41
+ # filter locals
42
+ b_entry_name = k.split('.')[0..-2].join(',')
43
+ b_entry_name == entry_name
44
+ end
45
+ .reject { |k, _| interface_function?(k, registry) }
46
+ .reject { |k, _| interop_function?(k, registry) }
47
+ .values
48
+ .flat_map(&:constraints)
49
+ .flat_map { Constraints.solve_at_finalize(it, registry, entry_name) }
50
+
51
+ # TODO: impl declarations need their own finalization pass here.
52
+ # Unresolved constraints from impl function bodies (e.g. Eq(a) from
53
+ # `one.id == other.id` inside `impl Eq for Pepe(a)`) should be promoted
54
+ # to impl-level requirements — making the impl an ImplementationTemplate
55
+ # with those constraints — rather than being dropped silently.
56
+
57
+ state
58
+ .with(errors: state.errors + errors)
59
+ .to_result
60
+ end
61
+
62
+ def interface_function?(qname, registry)
63
+ *module_parts, name = qname.split('.')
64
+ Symbol
65
+ .value_ref(module_parts.join('.'), name)
66
+ .then { registry.lookup(it) }
67
+ .is_a?(Symbol::InterfaceFunction)
68
+ end
69
+
70
+ # Polymorphic-port constraints represent caller requirements
71
+ # (resolved at each call site), not body obligations to verify here.
72
+ def interop_function?(qname, registry)
73
+ *module_parts, name = qname.split('.')
74
+ Symbol
75
+ .value_ref(module_parts.join('.'), name)
76
+ .then { registry.lookup(it) }
77
+ .is_a?(Symbol::InteropFunction)
78
+ end
79
+
80
+ def check_repl(node, registry, env = Env.new)
81
+ check_node(node, registry, env, Expected.infer(env.fresh))
82
+ .to_result
83
+ end
84
+
85
+ def check_node(node, registry, state, expected_type)
86
+ case node
87
+ in AST::Body then Inference::Body
88
+ in AST::CaseOf then Inference::CaseOf
89
+ in AST::ConstructorReference then Inference::ConstructorReference
90
+ in AST::FunctionCall then Inference::FunctionCall
91
+ in AST::FunctionDeclaration then Inference::FunctionDeclaration
92
+ in AST::Grouping then Inference::Grouping
93
+ in AST::IfThenElse then Inference::IfThenElse
94
+ in AST::Implementation then Inference::Implementation
95
+ in AST::ImportDeclaration then Inference::ImportDeclaration
96
+ in AST::InteropImportDeclaration then Inference::InteropImportDeclaration
97
+ in AST::Lambda then Inference::Lambda
98
+ in AST::List then Inference::List
99
+ in AST::Literal | AST::CharLiteral then Inference::Literal
100
+ in AST::Module then Inference::Module
101
+ in AST::QualifiedAccess then Inference::QualifiedAccess
102
+ in AST::RecordAccess then Inference::RecordAccess
103
+ in AST::StructDeclaration then Inference::StructDeclaration
104
+ in AST::InterfaceDeclaration then Inference::InterfaceDeclaration
105
+ in AST::RecordField then Inference::RecordField
106
+ in AST::RecordLiteral then Inference::RecordLiteral
107
+ in AST::RecordUpdate then Inference::RecordUpdate
108
+ in AST::TypeDeclaration then Inference::TypeDeclaration
109
+ in AST::Assign then Inference::Assign
110
+ in AST::VariableReference then Inference::VariableReference
111
+ end
112
+ .infer(node, registry, state, expected_type)
113
+ .then { |s, r| [pin_node_type(s, node, r), r] }
114
+ end
115
+
116
+ private
117
+
118
+ # Records the inferred type at this node so post-finalize the LSP
119
+ # (and anyone else) can look up `node.id -> type`. Types contain
120
+ # Type::Var here; `canonicalize_node_types` resolves them once the
121
+ # substitution is final.
122
+ def pin_node_type(state, node, result)
123
+ return state unless node.respond_to?(:id) && result.respond_to?(:type)
124
+
125
+ state.with(env: state.env.pin_type(node.id, result.type))
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,41 @@
1
+ module Jade
2
+ module Frontend
3
+ # Emits a warning per module-private function that no reference in
4
+ # the module's own usage_index touches. Runs after UsageAnalysis so
5
+ # it can read the index. Never fails; only appends to diagnostics.
6
+ #
7
+ # Scope is intentionally narrow: only Symbol::Function for now. Other
8
+ # kinds (Constructor, Variant, Union, Struct, InterfaceFunction) need
9
+ # separate filter rules — e.g. variants are reachable via pattern
10
+ # match, interface fns are dispatched, not directly called — and are
11
+ # deferred.
12
+ module UnusedAnalysis
13
+ extend self
14
+
15
+ def analyze(entry, _registry)
16
+ entry
17
+ .defined_values
18
+ .values
19
+ .select { unused?(it, entry) }
20
+ .reduce(entry.diagnostics) { |list, sym| list.add(warning(sym, entry)) }
21
+ .then { entry.with(diagnostics: it) }
22
+ end
23
+
24
+ private
25
+
26
+ def unused?(symbol, entry)
27
+ symbol.is_a?(Jade::Symbol::Function) &&
28
+ symbol.decl_span &&
29
+ !entry.exposed_value(symbol.name) &&
30
+ !entry.usage_index.ever_referenced?(symbol)
31
+ end
32
+
33
+ def warning(symbol, entry)
34
+ Diagnostics::Diagnostic.warning(
35
+ "unused function `#{symbol.name}`",
36
+ primary: Diagnostics::Label[entry.source, symbol.decl_span, 'never used'],
37
+ )
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,53 @@
1
+ module Jade
2
+ module Frontend
3
+ module UsageAnalysis
4
+ Reference = Data.define(:symbol_key, :kind, :range)
5
+
6
+ ReferenceIndex = Data.define(:references) do
7
+ def initialize(references: {})
8
+ super
9
+ end
10
+
11
+ def self.empty
12
+ new(references: {})
13
+ end
14
+
15
+ def for(symbol)
16
+ references[ReferenceIndex.key_for(symbol)] || []
17
+ end
18
+
19
+ def passed_as_value?(symbol)
20
+ self.for(symbol).any? { it.kind == :as_value }
21
+ end
22
+
23
+ def ever_referenced?(symbol)
24
+ self.for(symbol).any?
25
+ end
26
+
27
+ def references_in(module_name)
28
+ references
29
+ .values
30
+ .flatten
31
+ .select { it.symbol_key.first == module_name }
32
+ end
33
+
34
+ # Locals key on `decl_span`; module-level symbols on
35
+ # `[module_name, name]`. NOTE: when `:type_annotation` refs land
36
+ # they may point at a `Symbol::Variable` that's a *type*
37
+ # variable, which would collide with value-level locals under
38
+ # `[:local, decl_span]`. Revisit the keying scheme then —
39
+ # likely split into `[:local_value, ...]` vs `[:local_type, ...]`.
40
+ def self.key_for(symbol)
41
+ case symbol
42
+ in Symbol::Variable
43
+ [:local, symbol.decl_span]
44
+ in Symbol::ValueRef | Symbol::TypeRef
45
+ [symbol.module_name, symbol.name]
46
+ else
47
+ [symbol.module_name, symbol.name]
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,195 @@
1
+ require 'jade/frontend/usage_analysis/reference_index'
2
+
3
+ module Jade
4
+ module Frontend
5
+ # Walks a resolved AST and builds a ReferenceIndex of where each
6
+ # symbol is used. Runs after SemanticAnalysis (which attaches symbols)
7
+ # and before TypeChecking. Never fails — attaches `usage_index` to
8
+ # the entry and returns it.
9
+ #
10
+ # Reference kinds:
11
+ # :called - in callee position of a FunctionCall
12
+ # :as_value - bare reference (passed as value, returned, etc.)
13
+ # :constructed - constructor applied with args
14
+ # :pattern_match - constructor used in a pattern match
15
+ # :type_annotation - type appearing in a signature, variant args,
16
+ # struct fields, interface signatures, etc.
17
+ # :exposed - name listed in `module M exposing (...)`
18
+ module UsageAnalysis
19
+ extend self
20
+
21
+ def analyze(entry, _registry)
22
+ walk(entry.ast, :as_value, entry)
23
+ .group_by(&:symbol_key)
24
+ .freeze
25
+ .then { entry.with(usage_index: ReferenceIndex.new(references: it)) }
26
+ end
27
+
28
+ private
29
+
30
+ def walk(node, ctx, entry)
31
+ case node
32
+ in AST::Module(exposing:, body:)
33
+ walk_exposing(exposing, entry) + walk(body, :as_value, entry)
34
+
35
+ in AST::Body(expressions:)
36
+ expressions.flat_map { walk(it, :as_value, entry) }
37
+
38
+ in AST::FunctionDeclaration(body:, params:, return_type:)
39
+ walk(body, :as_value, entry) +
40
+ params.flat_map { walk_type(it.type, entry) } +
41
+ walk_type(return_type, entry)
42
+
43
+ in AST::FunctionCall(callee:, args:)
44
+ walk(callee, :called, entry) + args.flat_map { walk(it, :as_value, entry) }
45
+
46
+ in AST::VariableReference(symbol:, range:)
47
+ ref(symbol, ctx, range)
48
+
49
+ in AST::ConstructorReference(symbol:, range:)
50
+ ref(symbol, ctx == :called ? :constructed : :as_value, range)
51
+
52
+ in AST::QualifiedAccess(symbol:, range:)
53
+ ref(symbol, ctx, range)
54
+
55
+ in AST::Lambda(body:, params:)
56
+ walk(body, :as_value, entry) +
57
+ params.flat_map { walk(it, :as_value, entry) }
58
+
59
+ in AST::Assign(pattern:, expression:)
60
+ walk(expression, :as_value, entry) + walk(pattern, :as_value, entry)
61
+
62
+ in AST::IfThenElse(condition:, if_branch:, else_branch:)
63
+ walk(condition, :as_value, entry) +
64
+ walk(if_branch, :as_value, entry) +
65
+ walk(else_branch, :as_value, entry)
66
+
67
+ in AST::CaseOf(expression:, branches:)
68
+ walk(expression, :as_value, entry) +
69
+ branches.flat_map { walk(it, :as_value, entry) }
70
+
71
+ in AST::CaseOfBranch(pattern:, body:)
72
+ walk(pattern, :as_value, entry) + walk(body, :as_value, entry)
73
+
74
+ in AST::Pattern::Constructor(constructor:, patterns:, symbol:)
75
+ # Don't walk `constructor` — it's a bare ConstructorReference
76
+ # and walking it would record a spurious :as_value for every
77
+ # pattern match.
78
+ ref(symbol, :pattern_match, constructor.range) +
79
+ patterns.flat_map { walk(it, :as_value, entry) }
80
+
81
+ in AST::Pattern::List(patterns:, rest:)
82
+ rest_refs = rest ? walk(rest, :as_value, entry) : []
83
+ patterns.flat_map { walk(it, :as_value, entry) } + rest_refs
84
+
85
+ in AST::Pattern::Record(fields:)
86
+ fields.flat_map { walk(it.pattern, :as_value, entry) }
87
+
88
+ in AST::Pattern::Literal | AST::Pattern::Binding | AST::Pattern::Wildcard
89
+ []
90
+
91
+ in AST::Grouping(expression:)
92
+ walk(expression, ctx, entry)
93
+
94
+ in AST::List(items:)
95
+ items.flat_map { walk(it, :as_value, entry) }
96
+
97
+ in AST::RecordLiteral(fields:)
98
+ fields.flat_map { walk(it, :as_value, entry) }
99
+
100
+ in AST::RecordUpdate(base:, fields:)
101
+ walk(base, :as_value, entry) + fields.flat_map { walk(it, :as_value, entry) }
102
+
103
+ in AST::RecordField(value:)
104
+ walk(value, :as_value, entry)
105
+
106
+ in AST::RecordAccess(target:)
107
+ walk(target, :as_value, entry)
108
+
109
+ in AST::Implementation(applied_type:, functions:)
110
+ walk_type(applied_type, entry) +
111
+ functions.flat_map { walk(it, :as_value, entry) }
112
+
113
+ in AST::ImplementationFunction(fn:)
114
+ walk(fn, :as_value, entry)
115
+
116
+ in AST::TypeDeclaration(variants:)
117
+ variants.flat_map { it.args.flat_map { walk_type(it, entry) } }
118
+
119
+ in AST::StructDeclaration(record_type:)
120
+ walk_type(record_type, entry)
121
+
122
+ in AST::InterfaceDeclaration(functions:)
123
+ functions.flat_map { walk_type(it.type, entry) }
124
+
125
+ in AST::InteropImportDeclaration(functions:)
126
+ functions.flat_map { walk_type(it.type, entry) }
127
+
128
+ in AST::ImportDeclaration | AST::VariantDeclaration |
129
+ AST::Literal | AST::CharLiteral |
130
+ AST::MemberAccess | AST::KeyedCall
131
+ # ImportDeclaration's exposing list is handled when we land
132
+ # in importer modules — see walk_exposing. KeyedCall and
133
+ # MemberAccess are lowered away during semantic_analysis, so
134
+ # the branches are defensive against partial ASTs.
135
+ []
136
+ end
137
+ end
138
+
139
+ def walk_type(node, entry)
140
+ case node
141
+ in nil
142
+ []
143
+
144
+ in AST::TypeName(type:, range:)
145
+ entry.types[type]
146
+ .then { it ? [Reference[ReferenceIndex.key_for(it), :type_annotation, range]] : [] }
147
+
148
+ in AST::TypeApplication(constructor:, args:)
149
+ walk_type(constructor, entry) + args.flat_map { walk_type(it, entry) }
150
+
151
+ in AST::TypeFunction(params:, return_type:)
152
+ params.flat_map { walk_type(it, entry) } + walk_type(return_type, entry)
153
+
154
+ in AST::TypeRecord(fields:)
155
+ fields.values.flat_map { walk_type(it, entry) }
156
+
157
+ in AST::TypeTuple(items:)
158
+ items.flat_map { walk_type(it, entry) }
159
+
160
+ in AST::TypeVar | AST::TypeUnit | AST::QualifiedTypeName |
161
+ AST::TypeParam
162
+ []
163
+ end
164
+ end
165
+
166
+ def walk_exposing(node, entry)
167
+ case node
168
+ in AST::ExposeList(items:)
169
+ items.flat_map { walk_expose_item(it, entry) }
170
+
171
+ in AST::ExposeAll | AST::ExposeNone
172
+ []
173
+ end
174
+ end
175
+
176
+ def walk_expose_item(item, entry)
177
+ case item
178
+ in AST::ExposeValue(name:, range:)
179
+ exposed_ref(entry.lookup_value(name), range)
180
+
181
+ in AST::ExposeType | AST::ExposeTypeExpand
182
+ exposed_ref(entry.lookup_type(item.name), item.range)
183
+ end
184
+ end
185
+
186
+ def exposed_ref(symbol, range)
187
+ symbol ? [Reference[ReferenceIndex.key_for(symbol), :exposed, range]] : []
188
+ end
189
+
190
+ def ref(symbol, kind, range)
191
+ [Reference[ReferenceIndex.key_for(symbol), kind, range]]
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,101 @@
1
+ require 'jade/registry'
2
+ require 'jade/symbol'
3
+ require 'jade/type'
4
+ require 'jade/stdlib'
5
+ require 'jade/did_you_mean'
6
+
7
+ require 'jade/frontend/comment_attacher'
8
+ require 'jade/frontend/forward_declaration'
9
+ require 'jade/frontend/semantic_analysis'
10
+ require 'jade/frontend/usage_analysis'
11
+ require 'jade/frontend/unused_analysis'
12
+ require 'jade/frontend/type_checking'
13
+ require 'jade/frontend/fixity_fixer'
14
+ require 'jade/frontend/desugaring'
15
+ require 'jade/frontend/desugaring/resolved'
16
+
17
+ module Jade
18
+ module Frontend
19
+ extend self
20
+
21
+ # On error, wraps the failure as `[latest_processed_entry, errors]` so
22
+ # tolerant callers can recover the AST as of the last successful stage
23
+ # instead of falling back to the original pre-frontend entry.
24
+ def run_entry(initial, registry)
25
+ latest = initial
26
+ capture = ->(entry) { latest = entry }
27
+
28
+ initial
29
+ .then { FixityFixer.fix_entry(it).tap(&capture) }
30
+ .then { Desugaring.desugar_entry(it).tap(&capture) }
31
+ .then { ForwardDeclaration.declare_entry(it, registry).map { it.tap(&capture) } }
32
+ .and_then { SemanticAnalysis.analyze(it, registry.update_module(it)).map { it.tap(&capture) } }
33
+ .map { Desugaring.desugar_resolved_entry(it, registry.update_module(it)).tap(&capture) }
34
+ .map { UsageAnalysis.analyze(it, registry.update_module(it)).tap(&capture) }
35
+ .map { UnusedAnalysis.analyze(it, registry.update_module(it)).tap(&capture) }
36
+ .and_then { TypeChecking.check(it, registry.update_module(it)).map { it.tap(&capture) } }
37
+ .map_error { |errs| [latest, errs] }
38
+ end
39
+
40
+ def run(ast)
41
+ run_up_to_semantic_analysis(ast)
42
+ .and_then do |(entry, registry)|
43
+ TypeChecking
44
+ .check(entry, registry)
45
+ .map { [it.ast, registry.update_module(it)] }
46
+ end
47
+ end
48
+
49
+ def run_repl(ast, registry, current_entry, scope, env, var_gen)
50
+ registry ||= registry_with_basics
51
+ current_entry ||= entry_with_basics('JadeRepl')
52
+ scope ||= SemanticAnalysis::Scope.new
53
+ env ||= TypeChecking::Env.new
54
+ var_gen ||= TypeChecking::VarGen.new
55
+
56
+ ForwardDeclaration
57
+ .declare(ast, registry, current_entry)
58
+ .then { |entry| FixityFixer.fix(ast).then { [it, entry] } }
59
+ .then do |fixed_ast, updated_entry|
60
+ updated_registry = registry.update_module(updated_entry)
61
+ SemanticAnalysis
62
+ .analyze_repl(fixed_ast, updated_registry, scope, updated_entry)
63
+ .and_then do |(enhanced_ast, new_scope)|
64
+ enhanced_ast = Desugaring.desugar_resolved(enhanced_ast, updated_registry)
65
+ TypeChecking.check_repl(enhanced_ast, updated_registry, env, var_gen)
66
+ .map { |type, new_env| [enhanced_ast, type, updated_registry, updated_entry, new_scope, new_env] }
67
+ end
68
+ end
69
+ end
70
+
71
+ def run_up_to_semantic_analysis(ast)
72
+ registry, current_entry = entry_with_basics(ast)
73
+
74
+ FixityFixer.fix(ast)
75
+ .then { Desugaring.desugar(it) }
76
+ .then { |enh_ast| ForwardDeclaration.declare(enh_ast, registry, current_entry).map { [enh_ast, it] } }
77
+ .and_then do |enh_ast, entry|
78
+ SemanticAnalysis.analyze(entry.with(ast: enh_ast), registry.update_module(entry))
79
+ end
80
+ .map { Desugaring.desugar_resolved_entry(it, registry.update_module(it)) }
81
+ .map { UsageAnalysis.analyze(it, registry.update_module(it)) }
82
+ .map { [it, registry.update_module(it)] }
83
+ end
84
+
85
+ def entry_with_basics(ast)
86
+ entry =
87
+ case ast
88
+ in AST::Module(name:)
89
+ name
90
+ else
91
+ '__Test__'
92
+ end
93
+ .then { Registry.entry(it).with(ast:) }
94
+
95
+ Stdlib.load(Registry.new)
96
+ .add_module(entry)
97
+ .then { Stdlib.apply(it) }
98
+ .then { [it, it.modules[entry.name]] }
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,68 @@
1
+ require 'jade/decode'
2
+ require 'jade/result'
3
+ require 'jade/interop/error'
4
+
5
+ module Jade
6
+ module Interop
7
+ module Boundary
8
+ extend self
9
+
10
+ # Boundary-side decode: succeed → return the value, fail → raise. The
11
+ # Result wrap/unwrap that user-level Decode.from_value uses is dead
12
+ # weight at the boundary because failure always raises anyway. Skipping
13
+ # it removes one allocation per arg per Ruby→Jade call.
14
+ def decode_or_raise(decoder, value)
15
+ case Jade::Decode::Runner.run(decoder, value)
16
+ in Jade::Result::Ok[v] then v
17
+ in Jade::Result::Err[e] then raise Jade::Interop::DecodeError.new(e, value)
18
+ end
19
+ end
20
+
21
+ # Specialized fast-path validators. Emitted by codegen for known-shape
22
+ # argument types in place of the generic `decode_or_raise` path —
23
+ # avoids constructing a Decoder descriptor and walking the
24
+ # interpreter for primitives.
25
+ def integer(label, v)
26
+ ::Integer === v ? v : type_error!(label, v)
27
+ end
28
+
29
+ def string(label, v)
30
+ ::String === v ? v.dup : type_error!(label, v)
31
+ end
32
+
33
+ def bool(label, v)
34
+ v == true || v == false ? v : type_error!(label, v)
35
+ end
36
+
37
+ def float(label, v)
38
+ ::Numeric === v ? v.to_f : type_error!(label, v)
39
+ end
40
+
41
+ def list_of(klass, label, v)
42
+ v.is_a?(::Array) && v.all? { klass === _1 } ? v : type_error!(label, v)
43
+ end
44
+
45
+ # Validates that v is an Array but doesn't check element types — used
46
+ # when the per-element decoder isn't a simple `is_a?` (e.g. nested
47
+ # structs). The caller maps a decoder over the result.
48
+ def array(label, v)
49
+ v.is_a?(::Array) ? v : type_error!(label, v)
50
+ end
51
+
52
+ def hash(label, v)
53
+ case v
54
+ when ::Hash then v
55
+ when ::Data then v.to_h.transform_keys(&:to_s)
56
+ else type_error!(label, v)
57
+ end
58
+ end
59
+
60
+ def type_error!(label, v)
61
+ raise Jade::Interop::DecodeError.new(
62
+ Jade::Decode::WrongType[label, Jade::Decode.type_name(v)],
63
+ v,
64
+ )
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,84 @@
1
+ module Jade
2
+ module Interop
3
+ class Error < StandardError; end
4
+
5
+ class PortNotRegistered < Error
6
+ def initialize(module_name, function_name)
7
+ super(
8
+ "Port `#{function_name}` on `#{module_name}` is not a Jade port. " \
9
+ "Add `extend Jade::Port` to `#{module_name}` and declare it with " \
10
+ "`task :#{function_name} do |t, ...| ... end`."
11
+ )
12
+ end
13
+ end
14
+
15
+ # Raised when a Ruby caller invokes a Jade function whose signature
16
+ # has no public boundary — typically a polymorphic fn, a fn with
17
+ # function-typed args, or a fn over a type whose Decodable/Encodable
18
+ # can't be derived. The user's options are to add an explicit
19
+ # `implements Decodable/Encodable`, restructure the signature with
20
+ # decodable types, or accept that the fn is Jade-internal only.
21
+ class NotExposed < Error
22
+ def initialize(module_name:, function_name:, hint: nil)
23
+ ["#{module_name}.#{function_name} is not exposed to Ruby.", hint]
24
+ .compact
25
+ .join(' ')
26
+ .then { super(it) }
27
+ end
28
+ end
29
+
30
+ # Raised by bang-suffixed Task wrappers when the underlying Task ran
31
+ # to the Err arm. The encoded err value is available on `.error` for
32
+ # structured handling — pattern-match on it for shape-specific
33
+ # recovery, or just inspect for logging.
34
+ class TaskError < Error
35
+ attr_reader :error
36
+
37
+ def initialize(error)
38
+ @error = error
39
+ super("Task returned an error: #{error.inspect}")
40
+ end
41
+ end
42
+
43
+ class DecodeError < Error
44
+ attr_reader :decode_error, :value
45
+
46
+ def initialize(decode_error, value)
47
+ @decode_error = decode_error
48
+ @value = value
49
+ super(format(decode_error, value))
50
+ end
51
+
52
+ private
53
+
54
+ def format(error, value)
55
+ path, leaf = unwind(error)
56
+ location = path.empty? ? "value" : path.join
57
+
58
+ case leaf
59
+ in Jade::Decode::WrongType[expected, got]
60
+ "Port returned a value that failed to decode at #{location}: expected #{expected}, got #{got} (#{value.inspect})"
61
+
62
+ in Jade::Decode::MissingField[key]
63
+ "Port returned a value that failed to decode at #{location}: missing field `#{key}` (#{value.inspect})"
64
+
65
+ in Jade::Decode::Custom[msg]
66
+ "Port returned a value that failed to decode at #{location}: #{msg} (#{value.inspect})"
67
+
68
+ in Jade::Decode::Multiple[errors]
69
+ errors
70
+ .map { format(it, value) }
71
+ .join("; ")
72
+ end
73
+ end
74
+
75
+ def unwind(error, path = [])
76
+ case error
77
+ in Jade::Decode::AtField[key, inner] then unwind(inner, path + [".#{key}"])
78
+ in Jade::Decode::AtIndex[idx, inner] then unwind(inner, path + ["[#{idx}]"])
79
+ else [path, error]
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end