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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ae124aca6294ee2cc9d2ed1cb7cf05c7224aaa429277b8e3c883f4735e11aa9b
4
+ data.tar.gz: 35e00d927319976b642638959808b586214c08bf084135197099d88270d776c6
5
+ SHA512:
6
+ metadata.gz: 57bc05a0cbabfa09e033aa3c6627fcbefdf08b436b8ec20c4fe6a6d9e702f51c4f9a39ad783530896f1cc640a97fe649da79c00a43c30c82478747ec00f98017
7
+ data.tar.gz: db704c4e1c98eef3d9fd5a0df63539a3128e2c8f08976004691b281967f8f3621fd5e6969313d53df2c17b55195a4f8b0d27d69fc33001973428a0ab86b561d4
data/CHANGELOG.md ADDED
@@ -0,0 +1,23 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
5
+ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0]
10
+
11
+ Initial release.
12
+
13
+ - Functional, type-safe language that compiles to readable Ruby.
14
+ - Hindley–Milner type inference with union types, records, and pattern
15
+ matching.
16
+ - Standard library (`Maybe`, `Result`, `List`, `String`, `Dict`, and more).
17
+ - Interfaces with instance resolution (`Eq`, `Comparable`).
18
+ - Ruby interop with typed boundaries (`uses ... with`).
19
+ - `jade` CLI dispatcher: `fmt`, `lsp`, `q`.
20
+ - Language server and headless query interface for editor/agent tooling.
21
+
22
+ [Unreleased]: https://github.com/agustinrhcp/jade/compare/v0.1.0...HEAD
23
+ [0.1.0]: https://github.com/agustinrhcp/jade/releases/tag/v0.1.0
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Agustin Cornu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,386 @@
1
+ # Jade
2
+
3
+ [![CI](https://github.com/agustinrhcp/jade/actions/workflows/ci.yml/badge.svg)](https://github.com/agustinrhcp/jade/actions/workflows/ci.yml)
4
+
5
+ A statically typed, functional language that compiles to readable Ruby.
6
+ Inspired by Elm. Type inference, union types, exhaustive pattern matching, and
7
+ typed boundaries to Ruby.
8
+
9
+ ## What it looks like
10
+
11
+ ```jade
12
+ module Greeter exposing (greet)
13
+
14
+ def greet(name: Maybe(String)) -> String
15
+ case name
16
+ in Just(n) then "Hello, " ++ n
17
+ in Nothing then "Hello, stranger"
18
+ end
19
+ end
20
+ ```
21
+
22
+ compiles to:
23
+
24
+ ```ruby
25
+ module Greeter
26
+ extend self
27
+
28
+ module Internal
29
+ extend self
30
+
31
+ def greet(name)
32
+ case name
33
+ in Jade::Maybe::Just(n) then ("Hello, " + n)
34
+ in Jade::Maybe::Nothing then "Hello, stranger"
35
+ end
36
+ end
37
+ end
38
+
39
+ def self.greet(name)
40
+ # validate the incoming Ruby value, then call the pure function
41
+ Internal.greet(decode(name))
42
+ end
43
+ end
44
+ ```
45
+
46
+ There's no runtime VM and no FFI. The pure logic lives in `Internal`; the public
47
+ `Greeter.greet` decodes the untrusted Ruby argument (`nil`-or-`String`) into a
48
+ `Maybe` before handing it to the typed core. Calling it from Ruby:
49
+
50
+ ```ruby
51
+ Greeter.greet("Ada") # => "Hello, Ada"
52
+ Greeter.greet(nil) # => "Hello, stranger"
53
+ ```
54
+
55
+ ## Features
56
+
57
+ **`Maybe` instead of `nil`.** `Maybe(a)` makes absence explicit, and the
58
+ compiler flags any `case` that forgets the `Nothing` branch. Errors are values
59
+ too:
60
+
61
+ ```jade
62
+ module Accounts exposing (withdraw)
63
+
64
+ def withdraw(balance: Int, amount: Int) -> Result(Int, String)
65
+ amount > balance ? Err("insufficient funds") : Ok(balance - amount)
66
+ end
67
+ ```
68
+
69
+ **Union types and exhaustive pattern matching.** Add a variant and every
70
+ `case` that needs a new branch becomes a compile error:
71
+
72
+ ```jade
73
+ module Shapes exposing (area_of)
74
+
75
+ type Shape
76
+ = Circle(Float)
77
+ | Rectangle(Float, Float)
78
+
79
+
80
+ def area_of(shape: Shape) -> Float
81
+ case shape
82
+ in Circle(r) then 3.14 * r * r
83
+ in Rectangle(w, h) then w * h
84
+ end
85
+ end
86
+ ```
87
+
88
+ **Records with structural update and field access:**
89
+
90
+ ```jade
91
+ module Users exposing (birthday, name_of)
92
+
93
+ struct User = {
94
+ name: String,
95
+ age: Int
96
+ }
97
+
98
+
99
+ def birthday(user: User) -> User
100
+ { user | age: user.age + 1 }
101
+ end
102
+
103
+
104
+ def name_of(user: User) -> String
105
+ user.name
106
+ end
107
+ ```
108
+
109
+ **Pipes.** `|>` passes a value into the next function:
110
+
111
+ ```jade
112
+ module Pipeline exposing (shout)
113
+
114
+ def shout(words: List(String)) -> String
115
+ words
116
+ |> List.map(String.to_upper)
117
+ |> String.join(" ")
118
+ end
119
+ ```
120
+
121
+ Everything above is inferred end to end — annotations on `def` signatures are
122
+ checked, not required internally.
123
+
124
+ ## More of the language
125
+
126
+ **Lambdas and currying.** Lambdas are `(params) -> { body }`. A `_` in an
127
+ argument position curries that call — each `_` becomes a parameter, left to
128
+ right, so `discount(10, _)` is a one-argument function:
129
+
130
+ ```jade
131
+ module Pricing exposing (net)
132
+
133
+ def discount(rate: Int, price: Int) -> Int
134
+ price - price * rate / 100
135
+ end
136
+
137
+
138
+ def net(prices: List(Int)) -> List(Int)
139
+ prices
140
+ |> List.map(discount(10, _))
141
+ |> List.filter((p) -> { p > 50 })
142
+ end
143
+ ```
144
+
145
+ **Interfaces.** `==` / `!=` (Eq), `compare` (Comparable — returns `LT` / `EQ` /
146
+ `GT`), and `++` (Appendable) are built in and resolve from the argument types
147
+ at compile time, no annotation needed. You can define your own, with
148
+ implementations dispatched by type:
149
+
150
+ ```jade
151
+ module Shows exposing (describe)
152
+
153
+ struct Person = {
154
+ name: String,
155
+ age: Int
156
+ }
157
+
158
+
159
+ interface Show(a) with
160
+ show : a -> String
161
+ end
162
+
163
+
164
+ implements Show(Person) with
165
+ show: (p) -> { p.name ++ " (" ++ String.from_int(p.age) ++ ")" }
166
+ end
167
+
168
+
169
+ def describe(p: Person) -> String
170
+ show(p)
171
+ end
172
+ ```
173
+
174
+ **Modules and imports.** One module per file; `exposing` lists the public
175
+ surface. Pull names in by module, or selectively:
176
+
177
+ ```jade
178
+ module App exposing (run)
179
+
180
+ import Mathx exposing (double)
181
+
182
+
183
+ def run(n: Int) -> Int
184
+ double(n) + 1
185
+ end
186
+ ```
187
+
188
+ ## JSON
189
+
190
+ `Decode.from_json` derives a decoder from the return type and `Encode.encode`
191
+ derives the encoder, so a struct round-trips without a hand-written decoder or
192
+ encoder. A missing field comes back as an `Err`, not a `nil`:
193
+
194
+ ```jade
195
+ module Api exposing (parse, render)
196
+
197
+ import Encode
198
+ import Decode exposing (DecodeError)
199
+
200
+
201
+ struct User = {
202
+ name: String,
203
+ age: Int
204
+ }
205
+
206
+
207
+ def parse(json: String) -> Result(User, DecodeError)
208
+ Decode.from_json(json)
209
+ end
210
+
211
+
212
+ def render(user: User) -> String
213
+ Encode.encode_to_string(Encode.encode(user))
214
+ end
215
+ ```
216
+
217
+ ```ruby
218
+ Api::Internal.parse('{"name":"Ada","age":40}')
219
+ # => Ok(User(name: "Ada", age: 40))
220
+
221
+ Api::Internal.parse('{"name":"Ada"}')
222
+ # => Err(MissingField("age"))
223
+
224
+ Api::Internal.render(Api::User.new(name: "Ada", age: 40))
225
+ # => "{\"name\":\"Ada\",\"age\":40}"
226
+ ```
227
+
228
+ When you need them, the pieces are explicit too: `Decode.field`,
229
+ `Decode.list`, and `Decode.succeed(User(_, _)) |> Decode.required(...)` build
230
+ decoders by hand — the `_` currying again.
231
+
232
+ ## Side-effect-free testing
233
+
234
+ Jade functions are pure; effects go through `Task`, declared in a `uses`
235
+ block. A function that doesn't use a `Task` takes data in and returns data, so
236
+ you test it by passing values and asserting on the result — no mocks.
237
+
238
+ When you do hit a boundary, you stub the `Task`. Here's a Jade module that
239
+ calls a Ruby mailer, and the RSpec that drives it:
240
+
241
+ ```jade
242
+ # src/signup.jd
243
+ module Signup exposing (run)
244
+
245
+ uses Mailer with
246
+ deliver : String -> Task(Bool, String)
247
+ end
248
+
249
+
250
+ def run(email: String) -> Task(Bool, String)
251
+ deliver(email)
252
+ end
253
+ ```
254
+
255
+ ```ruby
256
+ # spec/signup_spec.rb
257
+ require 'jade'
258
+ require 'jade/tasks'
259
+ require 'jade/tasks/rspec'
260
+
261
+ Jade.setup { |c| c.source_root = 'src' }
262
+
263
+ module Mailer
264
+ extend Jade::Port
265
+ task(:deliver) { |t, email| t.ok(Mail.welcome(email).deliver) }
266
+ end
267
+
268
+ Jade.require('signup')
269
+
270
+ RSpec.describe 'Signup' do
271
+ include Jade::Tasks::RSpec
272
+
273
+ it 'sends a welcome mail to the new address' do
274
+ all_calls_to(Mailer.deliver) { |t, _email| t.ok(true) }
275
+
276
+ expect(Signup::Internal.run('ada@example.com').run).to be_ok(true)
277
+ expect(Mailer.deliver).to have_been_called.with('ada@example.com')
278
+ end
279
+
280
+ it 'surfaces a delivery failure as Err' do
281
+ all_calls_to(Mailer.deliver) { |t, _email| t.err("smtp down") }
282
+
283
+ expect(Signup::Internal.run('ada@example.com').run).to be_err("smtp down")
284
+ end
285
+ end
286
+ ```
287
+
288
+ `all_calls_to` sets a persistent stub; `next_call_to` queues one-shot answers.
289
+ `have_been_called` chains `.with(...)`, `.once`, `.times(n)`. Matchers include
290
+ `be_ok`, `be_err`, `be_just`, and `be_nothing`. Because effects only happen
291
+ through `Task`, a function's return type tells you whether it performs IO.
292
+
293
+ ## Using Jade from Ruby
294
+
295
+ The gem is `jade-lang`; the library you `require` is still `jade`. A RubyGems
296
+ release is coming — for now, install from a path or a git ref:
297
+
298
+ ```ruby
299
+ # Gemfile
300
+ gem 'jade-lang', path: '../jade'
301
+ # or: gem 'jade-lang', git: 'https://github.com/agustinrhcp/jade'
302
+ ```
303
+
304
+ Point it at your source, then `require` modules by name. Jade compiles each
305
+ `.jd` to `.jade/build/<module>.rb` on first require (and only when the source
306
+ is newer), then loads it:
307
+
308
+ ```ruby
309
+ require 'jade'
310
+
311
+ Jade.setup do |config|
312
+ config.source_root = 'src' # where your .jd files live
313
+ # config.build_dir = '.jade/build' (default)
314
+ end
315
+
316
+ Jade.require('greeter')
317
+
318
+ Greeter.greet('Ada') # => "Hello, Ada"
319
+ ```
320
+
321
+ Existing Ruby calls into Jade, and Jade calls into Ruby through `uses` blocks.
322
+ It's plain Ruby on disk, so it sits inside a Rails app like any other file.
323
+
324
+ ## If it doesn't work out
325
+
326
+ Run the compiler one last time, commit the generated `.rb`, and drop the `.jd`
327
+ files. The output is already plain Ruby — no rewrite, no migration. The
328
+ `jade-eject` skill automates removing the gem dependency, but it isn't required.
329
+
330
+ Worst case: you wrote Ruby with a nicer authoring layer for a while.
331
+
332
+ ## Editors and agents
333
+
334
+ There's a language server — type errors, inferred types, and jump-to-definition
335
+ in any editor that speaks LSP. For tools that don't, `jade q` answers the same
336
+ questions as one-shot JSON (hover, definition, references, symbols).
337
+
338
+ In our experience coding agents like Claude Code and Cursor handle Jade well:
339
+ the syntax is close enough to the ML family (Elm, OCaml, Haskell) that models
340
+ have useful priors, and the generated Ruby gives them a second source of truth
341
+ to check against. No promises that holds for every model — it's just held up
342
+ for us so far.
343
+
344
+ ## Tooling
345
+
346
+ A single `jade` binary fronts the toolchain:
347
+
348
+ ```
349
+ jade fmt [-i|-c] [file] # format .jd source (stdin or file)
350
+ jade lsp # language server over stdio (hover, defn, refs, diagnostics)
351
+ jade q hover FILE:L:C # headless JSON queries — hover/symbols/defn/refs
352
+ ```
353
+
354
+ `jade fmt` is deterministic and idempotent; wire it into your editor or a
355
+ pre-commit hook.
356
+
357
+ ## Standard library
358
+
359
+ `Basics`, `String`, `Char`, `List`, `Dict`, `Set`, `Tuple`, `Maybe`, `Result`,
360
+ `Task`, `Decode`, `Encode`, `Bytes`, `Calendar`, `Clock`. Stdlib operations
361
+ compile inline rather than through a runtime dispatch layer.
362
+
363
+ ## Docs
364
+
365
+ - [docs/syntax.md](docs/syntax.md) — the full language tour
366
+ - [docs/interop.md](docs/interop.md) — the Ruby boundary: ports, decoding, what crosses
367
+ - [docs/json.md](docs/json.md) — `Decode` / `Encode`, by hand and auto-derived
368
+ - [docs/testing.md](docs/testing.md) — stubbing Tasks, the RSpec matchers
369
+ - [docs/stdlib.md](docs/stdlib.md) — module-by-module map
370
+ - [docs/lsp.md](docs/lsp.md) — language server and `jade q` setup
371
+ - [examples/](examples/) — runnable `.jd` files
372
+
373
+ ## Status
374
+
375
+ Early and experimental — being tried out on small projects.
376
+
377
+ **In progress:** `Comparable` / `Show` derivation for user types, partial
378
+ record types in signatures, a stable REPL.
379
+
380
+ **Not great for:** throwaway scripts, libraries you ship to other Ruby
381
+ projects (they'd inherit the dependency), and performance-critical hot paths
382
+ (output is YJIT-friendly but unbenchmarked).
383
+
384
+ ## License
385
+
386
+ [MIT](LICENSE).
data/exe/jade ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'jade/cli'
5
+
6
+ Jade::CLI.run(ARGV)
@@ -0,0 +1,44 @@
1
+ module Jade
2
+ module AST
3
+ module Node
4
+ @@_next_id = 0
5
+
6
+ # Boilerplate added by Nodes.define alongside the AST-specific fields.
7
+ # Excluded from child traversal so we don't iterate the `range` Range
8
+ # as integers or descend into comment metadata.
9
+ BOILERPLATE_FIELDS = %i[
10
+ range symbol id
11
+ leading_comments trailing_comments dangling_comments
12
+ trailing_comma
13
+ ].freeze
14
+
15
+ def self.next_id
16
+ @@_next_id += 1
17
+ end
18
+
19
+ # Deepest descendant whose range covers `offset`, or self if no child
20
+ # matches. Returns nil if this node's range is missing or doesn't
21
+ # cover the offset.
22
+ def find_at(offset)
23
+ find_at_path(offset).last
24
+ end
25
+
26
+ # Path of nested nodes from self down to the deepest descendant
27
+ # covering `offset`. Empty if self doesn't cover. Use this when you
28
+ # need to consult ancestors — e.g. hover on `String` inside
29
+ # `String.length(...)` needs the surrounding QualifiedAccess, not the
30
+ # raw ConstructorReference.
31
+ def find_at_path(offset)
32
+ return [] unless range&.cover?(offset)
33
+
34
+ (members - BOILERPLATE_FIELDS)
35
+ .flat_map { public_send(it) }
36
+ .flat_map { it.is_a?(Array) ? it : [it] }
37
+ .filter_map { it.is_a?(Node) ? it.find_at_path(offset) : nil }
38
+ .reject(&:empty?)
39
+ .first
40
+ .then { [self] + (it || []) }
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,35 @@
1
+ require 'jade/ast/node'
2
+
3
+ module Jade
4
+ module AST
5
+ module Nodes
6
+ def define(name, *fields)
7
+ Data.define(
8
+ *fields,
9
+ :range,
10
+ :symbol,
11
+ :id,
12
+ :leading_comments,
13
+ :trailing_comments,
14
+ :dangling_comments,
15
+ :trailing_comma,
16
+ :dictionaries
17
+ ) {
18
+ include Node
19
+
20
+ define_method(:initialize) do |**kwargs|
21
+ kwargs[:symbol] ||= nil
22
+ kwargs[:id] ||= Node.next_id
23
+ kwargs[:leading_comments] ||= []
24
+ kwargs[:trailing_comments] ||= []
25
+ kwargs[:dangling_comments] ||= []
26
+ kwargs[:trailing_comma] ||= false
27
+ kwargs[:dictionaries] ||= []
28
+ super(**kwargs)
29
+ end
30
+ }
31
+ .then { const_set(name, it) }
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,50 @@
1
+ module Jade
2
+ module AST
3
+ module PrettyPrinter
4
+ extend self
5
+
6
+ def print(node, indent = 0)
7
+ prefix = ' ' * indent
8
+
9
+ case node
10
+ in AST::VariableReference(name:)
11
+ prefix + "Var(#{name})"
12
+
13
+ in AST::Assign(pattern:, expression:)
14
+ prefix + "VarBound(#{pattern} = " + print(expression, indent) + ")"
15
+
16
+ in AST::Literal(value:)
17
+ case value
18
+ in Integer | TrueClass | FalseClass
19
+ value.to_s
20
+
21
+ in String
22
+ "\"#{value}\""
23
+ end
24
+ in AST::FunctionCall(callee:, args:)
25
+ case callee
26
+ in AST::VariableReference(name:)
27
+ if is_infix?(name)
28
+ operator = name.delete_prefix('(').delete_suffix(')')
29
+
30
+ return prefix + "(#{print(args[0])} #{operator} #{print(args[1])})"
31
+ end
32
+ else
33
+ end
34
+
35
+ args
36
+ .map { print(it) }.join(', ')
37
+ .then { "(#{it})"}
38
+ .then { prefix + print(callee, indent) + it }
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def is_infix?(name)
45
+ name.start_with?('(') &&
46
+ name.end_with?(')')
47
+ end
48
+ end
49
+ end
50
+ end