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,34 @@
1
+ require 'set'
2
+
3
+ module Jade
4
+ module ModuleLoader
5
+ # Strips source positions from a nested Data tree so equivalent shapes
6
+ # produce equal byte sequences. Used to compute interface digests that
7
+ # survive whitespace and unrelated edits.
8
+ module Normalize
9
+ extend self
10
+
11
+ POSITION_FIELDS = %i[decl_span span range origin].freeze
12
+
13
+ def apply(value)
14
+ case value
15
+ when Data then normalize_data(value)
16
+ when Hash then value.transform_values { apply(it) }
17
+ when Array then value.map { apply(it) }
18
+ when Set then value.map { apply(it) }.to_set
19
+ else value
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def normalize_data(value)
26
+ value
27
+ .class
28
+ .members
29
+ .to_h { |m| [m, POSITION_FIELDS.include?(m) ? nil : apply(value.public_send(m))] }
30
+ .then { value.class.new(**it) }
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,41 @@
1
+ module Jade
2
+ module ModuleLoader
3
+ module TopologicalSort
4
+ extend self
5
+
6
+ def sort(graph)
7
+ graph
8
+ .nodes
9
+ .keys
10
+ .reduce([[], [], []]) { |state, node| visit(graph, state, node) }
11
+ .first
12
+ end
13
+
14
+ private
15
+
16
+ def visit(graph, state, node)
17
+ visited, visiting, stack = state
18
+
19
+ return state if visited.include?(node)
20
+ raise CycleDependencyError.new(stack + [node]) if visiting.include?(node)
21
+
22
+ new_visited, new_visiting, new_stack = graph
23
+ .nodes[node]
24
+ .reduce([visited, visiting + [node], stack + [node]]) do |new_state, neighbor|
25
+ visit(graph, new_state, neighbor)
26
+ end
27
+
28
+ [new_visited + [node], new_visiting - [node], new_stack[0...-1]]
29
+ end
30
+ end
31
+
32
+ class CycleDependencyError < StandardError
33
+ attr_reader :cycle_path
34
+
35
+ def initialize(cycle_path)
36
+ @cycle_path = cycle_path
37
+ super("Cycle detected in module dependencies: #{cycle_path.join(' -> ')}")
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,127 @@
1
+ require 'fileutils'
2
+
3
+ require 'jade/parsing'
4
+ require 'jade/lexer'
5
+ require 'jade/ast'
6
+ require 'jade/registry'
7
+ require 'jade/frontend'
8
+ require 'jade/codegen'
9
+ require 'jade/module_loader/cache'
10
+ require 'jade/module_loader/dependency_resolver'
11
+ require 'jade/module_loader/dependency_graph'
12
+ require 'jade/module_loader/normalize'
13
+ require 'jade/module_loader/topological_sort'
14
+ require 'jade/diagnostics'
15
+
16
+ require 'jade/stdlib'
17
+
18
+ module Jade
19
+ module ModuleLoader
20
+ extend self
21
+
22
+ def load(source_root, path, cache_dir: nil, tolerant: false, overlays: {})
23
+ Source.load(source_root, path, overlays:)
24
+ .then { load_(it, new_registry(source_root, overlays:), entry: true) }
25
+ .then { Stdlib.apply(it) }
26
+ .then { compile(it, cache_dir:, tolerant:) }
27
+ end
28
+
29
+ def load_import(module_name, registry)
30
+ return registry if registry.get(module_name)&.ast
31
+
32
+ Source
33
+ .load_from_module_name(registry.source_root, module_name, overlays: registry.overlays)
34
+ .then { load_(it, registry) }
35
+ end
36
+
37
+ def emit(registry, path: '.jade/build')
38
+ registry
39
+ .modules
40
+ .each_value
41
+ .reject { Stdlib.is_intrinsic?(it) }
42
+ .each { write_entry(it, path) }
43
+
44
+ registry
45
+ end
46
+
47
+ private
48
+
49
+ def compile(registry, cache_dir: nil, tolerant: false)
50
+ registry
51
+ .modules_in_topo_order
52
+ .reject { Stdlib.is_stdlib?(it) }
53
+ .reduce([registry, {}]) do |(acc, digests), entry|
54
+ compiled, digest = compile_with_cache(entry, acc, digests, cache_dir, tolerant)
55
+
56
+ [acc.update_module(compiled), digests.merge(entry.name => digest)]
57
+ end
58
+ .first
59
+ end
60
+
61
+ def compile_with_cache(entry, registry, digests, cache_dir, tolerant)
62
+ return [compile_one(entry, registry, tolerant:), nil] unless cache_dir
63
+
64
+ key = direct_deps(entry.name, registry)
65
+ .filter_map { |dep| digests[dep]&.then { [dep, it] } }
66
+ .then { Cache.compute_key(entry, it) }
67
+
68
+ Cache.read(cache_dir, entry.name, key) ||
69
+ compile_and_write(entry, registry, cache_dir, key, tolerant)
70
+ end
71
+
72
+ def compile_and_write(entry, registry, cache_dir, key, tolerant)
73
+ compile_one(entry, registry, tolerant:)
74
+ .then { [it, it.interface_digest] }
75
+ .tap { |(compiled, digest)| Cache.write(cache_dir, entry.name, compiled, key, digest) }
76
+ end
77
+
78
+ def write_entry(entry, path)
79
+ full = File.join(path, entry.path)
80
+ FileUtils.mkdir_p(File.dirname(full))
81
+ File.write(full, entry.generated)
82
+ end
83
+
84
+ def compile_one(entry, registry, tolerant: false)
85
+ Frontend
86
+ .run_entry(entry, registry)
87
+ .map { Codegen.generate_entry(it, registry.update_module(it)) }
88
+ .on_err do |(latest, errors)|
89
+ diagnostics = errors.reduce(Diagnostics::List.empty) { |list, err| list.add(err.to_diagnostic(registry)) }
90
+
91
+ tolerant ? Ok[latest.with(diagnostics:)] : raise(CompilationError.new(diagnostics))
92
+ end => Ok(compiled)
93
+
94
+ compiled
95
+ end
96
+
97
+ def direct_deps(module_name, registry)
98
+ registry.dependency_graph.nodes[module_name] || []
99
+ end
100
+
101
+ def load_(source, registry, entry: false)
102
+ Lexer.tokenize(source)
103
+ .then { Parsing.parse(it, source:) }
104
+ .on_err do |err|
105
+ Diagnostics::List
106
+ .empty
107
+ .add(err.to_diagnostic(source: source))
108
+ .then { raise CompilationError.new(it) }
109
+ end => Ok([raw_ast, comments])
110
+
111
+ Frontend::CommentAttacher
112
+ .attach(raw_ast, comments, source)
113
+ .then { Registry.entry(source.to_module_name).with(ast: it, source:, entry:) }
114
+ .then { registry.add_module(it) }
115
+ end
116
+
117
+ # Stdlib content is identical across calls; loading it dominates
118
+ # short LSP recompiles (~15ms on a tiny user file). Cache the
119
+ # post-Stdlib.load Registry once per process and stamp the per-call
120
+ # source_root + overlays on a copy. Invalidated only on restart —
121
+ # acceptable since compiler code changes need a restart anyway.
122
+ def new_registry(source_root, overlays: {})
123
+ @stdlib_base ||= Stdlib.load(Registry.new)
124
+ @stdlib_base.with(source_root:, overlays:)
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,291 @@
1
+ require 'jade/diagnostics'
2
+
3
+ module Jade
4
+ module Parsing
5
+ module Combinators
6
+ module Dsl
7
+ def parser(name, private: false, &block)
8
+ builder = :"_build_#{name}"
9
+ define_method(builder, &block)
10
+ send(:private, builder)
11
+ module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
12
+ def #{name}
13
+ @#{name} ||= #{builder}
14
+ end
15
+ RUBY
16
+ send(:private, name) if private
17
+ end
18
+ end
19
+ extend Dsl
20
+
21
+ def grouped(parser)
22
+ type(:lparen).skip >> parser >> type(:rparen).skip
23
+ end
24
+
25
+ def at_least_one(parser, separated_by: none.skip)
26
+ tail = separated_by >> sequence(parser, separated_by:)
27
+ parser >> optional(tail, default: [])
28
+ end
29
+
30
+ # Consume `parser` if it matches; if not, drop the slot from the
31
+ # surrounding `>>` tuple (no value, no failure).
32
+ def maybe(parser)
33
+ (parser | none).skip
34
+ end
35
+
36
+ # Consume `parser` if it matches; otherwise inject `default` as the
37
+ # value. Keeps tuple arity stable in `>>` chains.
38
+ def optional(parser, default: nil)
39
+ parser | none.map { default }
40
+ end
41
+
42
+ def sequence(parser, separated_by: none.skip)
43
+ (parser.map { [it] } >> many(separated_by >> parser))
44
+ .map { it.flatten(1) }
45
+ end
46
+
47
+ CommaList = Data.define(:items, :trailing_comma) do
48
+ def self.empty
49
+ new(items: [], trailing_comma: false)
50
+ end
51
+ end
52
+
53
+ def comma_sequence(parser)
54
+ inner = sequence(parser, separated_by: type(:comma).skip)
55
+ P.new do |state|
56
+ inner.call(state).and_then do |(items, state1)|
57
+ trailing, state2 =
58
+ if !state1.eof? && state1.current.type == :comma
59
+ [true, state1.advance]
60
+ else
61
+ [false, state1]
62
+ end
63
+
64
+ Ok[[CommaList.new(items:, trailing_comma: trailing), state2]]
65
+ end
66
+ end
67
+ end
68
+
69
+ parser(:empty_comma_list) { none.map { CommaList.empty } }
70
+
71
+ parser(:none) { P.new { |state| Ok[[nil, state]] } }
72
+
73
+
74
+ # Tolerant counterpart of `sequence`. Strict mode (the default) behaves
75
+ # identically; tolerant mode records a diagnostic and resumes at the
76
+ # next sync token instead of failing.
77
+ def recovering_sequence(parser, sync_types:)
78
+ P.new { recovering_step(parser, sync_types, it, []) }
79
+ end
80
+
81
+ def recovering_step(parser, sync_types, state, results)
82
+ return Ok[[results, state]] if state.eof?
83
+
84
+ case parser.call(state)
85
+ in Ok([value, next_state])
86
+ recovering_step(parser, sync_types, next_state, [*results, value])
87
+
88
+ in Err([err, err_state]) unless state.tolerant
89
+ err.committed? ? Err[[err, err_state]] : Ok[[results, state]]
90
+
91
+ in Err([err, err_state])
92
+ # Always advance at least one token so termination is guaranteed
93
+ # even when sync_types isn't ahead.
94
+ moved_past = err.committed? && err_state.position > state.position
95
+ recovered = (moved_past ? err_state : state.advance)
96
+ .skip_until(sync_types)
97
+ .add_diagnostic(err.to_diagnostic)
98
+
99
+ recovering_step(parser, sync_types, recovered, results)
100
+ end
101
+ end
102
+
103
+ def skip(parser)
104
+ parser.map { |_| :skip }
105
+ end
106
+
107
+ def many(parser)
108
+ P.new do |state|
109
+ oks = []
110
+ current = state
111
+ committed_err = nil
112
+
113
+ loop do
114
+ break if current.eof?
115
+
116
+ case parser.call(current)
117
+ in Ok([value, next_state])
118
+ oks << value
119
+ current = next_state
120
+ in Err([err, err_state])
121
+ committed_err = Err[[err, err_state]] if err.committed?
122
+ break
123
+ end
124
+ end
125
+
126
+ committed_err || Ok[[oks, current]]
127
+ end
128
+ end
129
+
130
+ def type(type)
131
+ (@types ||= {})[type] ||= P.new do |state|
132
+ if state.eof?
133
+ Err[[
134
+ Parsing::EOFError.new(
135
+ entry: state.entry,
136
+ span: nil,
137
+ expected: type,
138
+ ),
139
+ state,
140
+ ]]
141
+
142
+ elsif state.current.type == :invalid_op
143
+ Err[[
144
+ Parsing::InvalidOperatorError.new(
145
+ entry: state.entry,
146
+ span: state.current.range,
147
+ actual: state.current,
148
+ ),
149
+ state,
150
+ ]]
151
+
152
+ elsif state.current.type == type
153
+ Ok[([state.current, state.advance])]
154
+
155
+ else
156
+ Err[[
157
+ Parsing::UnexpectedTokenError.new(
158
+ entry: state.entry,
159
+ span: state.current.range,
160
+ actual: state.current,
161
+ expected: type,
162
+ ),
163
+ state,
164
+ ]]
165
+ end
166
+ end
167
+ end
168
+
169
+ def lazy(&block)
170
+ P.new do |input|
171
+ block.call.call(input)
172
+ end
173
+ end
174
+
175
+ State = Data.define(
176
+ :tokens, :position, :entry, :context_stack, :tolerant, :diagnostics,
177
+ :source,
178
+ ) do
179
+ def initialize(
180
+ tokens:,
181
+ entry:,
182
+ position: 0,
183
+ context_stack: [],
184
+ tolerant: false,
185
+ diagnostics: Diagnostics::List.empty,
186
+ source: nil
187
+ )
188
+ super
189
+ end
190
+
191
+ def current
192
+ tokens[position]
193
+ end
194
+
195
+ def advance(n = 1)
196
+ with(position: position + n)
197
+ end
198
+
199
+ def eof?
200
+ position >= tokens.length
201
+ end
202
+
203
+ # Two positions on the same source line. Uses the source's
204
+ # `line_starts` index; returns true when source is unavailable so
205
+ # callers degrade to "accept whatever parses" rather than spurious
206
+ # errors.
207
+ def same_line?(pos_a, pos_b)
208
+ return true unless source
209
+
210
+ starts = source.line_starts
211
+ starts.bsearch_index { |ls| ls > pos_a } ==
212
+ starts.bsearch_index { |ls| ls > pos_b }
213
+ end
214
+
215
+ def add_diagnostic(diagnostic)
216
+ with(diagnostics: diagnostics.add(diagnostic))
217
+ end
218
+
219
+ def skip_until(sync_types)
220
+ (position...tokens.length)
221
+ .find { sync_types.include?(tokens[it].type) }
222
+ .then { with(position: it || tokens.length) }
223
+ end
224
+ end
225
+
226
+ class P
227
+ def initialize(&block)
228
+ @fn = block
229
+ end
230
+
231
+ def call(tokens)
232
+ @fn.call(tokens)
233
+ end
234
+
235
+ def map(&block)
236
+ P.new do |state|
237
+ call(state)
238
+ .map { |(value, ok_state)| [block.call(value), ok_state] }
239
+ end
240
+ end
241
+
242
+ def map_error(&block)
243
+ P
244
+ .new do |state|
245
+ call(state)
246
+ .map_error { |(err, err_state)| [block.call(err), err_state] }
247
+ end
248
+ end
249
+
250
+ def |(other)
251
+ P.new do |state|
252
+ call(state)
253
+ .on_err do |(error, state2)|
254
+ if error.committed?
255
+ Err[[error, state2]]
256
+ else
257
+ other.call(state)
258
+ end
259
+ end
260
+ end
261
+ end
262
+
263
+ def >>(other)
264
+ P.new do |state|
265
+ call(state).and_then do |(value1, state1)|
266
+ other.call(state1)
267
+ .map do |(value2, state2)|
268
+ [[value1, value2].reject { it == :skip }.flatten(1), state2]
269
+ end
270
+ .map_error do |(err, err_state)|
271
+ [err, state]
272
+ end
273
+ end
274
+ end
275
+ end
276
+
277
+ def skip
278
+ self.map { |_| :skip }
279
+ end
280
+
281
+ def commit
282
+ map_error(&:commit)
283
+ end
284
+
285
+ def context(name)
286
+ map_error { |err| err.with_context(name) }
287
+ end
288
+ end
289
+ end
290
+ end
291
+ end
@@ -0,0 +1,154 @@
1
+ require 'jade/error'
2
+ require 'jade/lexer'
3
+
4
+ module Jade
5
+ module Parsing
6
+ class Error < Jade::Error
7
+ def initialize(entry:, span:, actual:, expected:, committed: false, context: [])
8
+ super(entry:, span:)
9
+ @actual = actual
10
+ @expected = expected
11
+ @committed = committed
12
+ @context = context
13
+ end
14
+
15
+ def committed?
16
+ @committed
17
+ end
18
+
19
+ def commit
20
+ @committed = true
21
+ self
22
+ end
23
+
24
+ def with_context(name)
25
+ self.class.new(
26
+ entry: @entry,
27
+ span: @span,
28
+ actual: @actual,
29
+ expected: @expected,
30
+ committed: @committed,
31
+ context: [name, *@context],
32
+ )
33
+ end
34
+
35
+ attr_reader :actual, :expected, :context
36
+
37
+ protected
38
+
39
+ def context_prefix
40
+ return "" if @context.empty?
41
+
42
+ "While parsing #{@context.join(' > ')}: "
43
+ end
44
+ end
45
+
46
+ class EOFError < Error
47
+ def initialize(entry:, span:, expected:, actual: nil, committed: false, context: [])
48
+ super
49
+ end
50
+
51
+ def message
52
+ "#{context_prefix}Unexpected end of input, expected #{expected}"
53
+ end
54
+ end
55
+
56
+ class UnexpectedTokenError < Error
57
+ def initialize(entry:, span:, actual:, expected:, committed: false, context: [])
58
+ super
59
+ end
60
+
61
+ def hint
62
+ return leading_pipe_hint if leading_pipe_in_type_decl?
63
+ return reserved_keyword_hint if reserved_keyword_as_name?
64
+ return colon_not_eq_hint if eq_where_colon_expected?
65
+ return record_eq_hint if eq_where_record_pipe_expected?
66
+ nil
67
+ end
68
+
69
+ def message
70
+ "#{context_prefix}Unexpected token #{actual.value.inspect}, expected #{expected}#{" #{hint}" if hint}"
71
+ end
72
+
73
+ def label
74
+ "unexpected #{@actual.value.inspect}"
75
+ end
76
+
77
+ private
78
+
79
+ def leading_pipe_in_type_decl?
80
+ actual.type == :pipe &&
81
+ expected == :constant &&
82
+ @context.include?('type declaration')
83
+ end
84
+
85
+ def leading_pipe_hint
86
+ "(leading `|` isn't supported — write `type Foo = A | B` with no `|` before the first variant)"
87
+ end
88
+
89
+ def reserved_keyword_as_name?
90
+ expected == :identifier && Jade::Lexer::KEYWORDS.include?(actual.value)
91
+ end
92
+
93
+ def reserved_keyword_hint
94
+ "(`#{actual.value}` is a reserved keyword — choose a different name)"
95
+ end
96
+
97
+ # `=` where `:` is expected: record field, param, or annotation —
98
+ # all written `name: value`. e.g. `{ d | f = x }` chokes on the colon.
99
+ def eq_where_colon_expected?
100
+ actual.type == :assign && expected == :colon
101
+ end
102
+
103
+ def colon_not_eq_hint
104
+ "(use `:`, not `=`)"
105
+ end
106
+
107
+ # A bare `{ f = x }` literal backtracks into the record-update parser
108
+ # and chokes on the missing `|`. The real mistake is `=` for `:`.
109
+ def eq_where_record_pipe_expected?
110
+ actual.type == :assign &&
111
+ expected == :pipe &&
112
+ !@context.include?('type declaration')
113
+ end
114
+
115
+ def record_eq_hint
116
+ "(record fields use `:`, not `=` — write `{ name: value }`)"
117
+ end
118
+ end
119
+
120
+ # Specific case-branch shape: `in <pat> <body>` on the same source
121
+ # line, no `then` between them. Almost always the user meant
122
+ # `in <pat> then <body>` for an inline branch.
123
+ class MissingThenError < Error
124
+ def message
125
+ "#{context_prefix}Case branch needs `then` before an inline body " \
126
+ "(got #{@actual.value.inspect} on the same line as `in`). " \
127
+ "Write `in <pat> then <expr>`, or put the body on the next " \
128
+ "line for the multi-statement form."
129
+ end
130
+
131
+ def label
132
+ "missing `then`"
133
+ end
134
+ end
135
+
136
+ class InvalidOperatorError < Error
137
+ HINTS = {
138
+ '/=' => 'Use `!=` for inequality.',
139
+ }.freeze
140
+
141
+ def initialize(entry:, span:, actual:, expected: nil, committed: true, context: [])
142
+ super
143
+ end
144
+
145
+ def hint
146
+ HINTS[actual.value]
147
+ end
148
+
149
+ def message
150
+ "#{context_prefix}Invalid operator #{actual.value.inspect}.#{" #{hint}" if hint}"
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,12 @@
1
+ require 'jade/parsing/combinators'
2
+
3
+ module Jade
4
+ module Parsing
5
+ module Token
6
+ extend Combinators::Dsl
7
+
8
+ parser(:identifier) { type(:identifier) }
9
+ parser(:constant) { type(:constant) }
10
+ end
11
+ end
12
+ end