jade-lang 0.8.0 → 0.10.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 (152) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +439 -2
  3. data/docs/interop.md +18 -1
  4. data/docs/stdlib.md +3 -1
  5. data/docs/syntax.md +96 -0
  6. data/lib/jade/api.rb +9 -0
  7. data/lib/jade/ast.rb +37 -1
  8. data/lib/jade/cli/eject.rb +132 -0
  9. data/lib/jade/cli.rb +2 -0
  10. data/lib/jade/codegen/boundary/specialized/list.rb +11 -7
  11. data/lib/jade/codegen/boundary/specialized/maybe.rb +2 -2
  12. data/lib/jade/codegen/boundary/specialized/record.rb +19 -4
  13. data/lib/jade/codegen/boundary/specialized/scalar.rb +2 -2
  14. data/lib/jade/codegen/boundary/specialized.rb +13 -5
  15. data/lib/jade/codegen/boundary.rb +12 -2
  16. data/lib/jade/codegen/context.rb +17 -0
  17. data/lib/jade/codegen/error.rb +1 -0
  18. data/lib/jade/codegen/function_call.rb +23 -7
  19. data/lib/jade/codegen/function_declaration.rb +25 -13
  20. data/lib/jade/codegen/helpers.rb +27 -4
  21. data/lib/jade/codegen/implementation.rb +2 -2
  22. data/lib/jade/codegen/inline.rb +1 -1
  23. data/lib/jade/codegen/inlines.rb +6 -1
  24. data/lib/jade/codegen/names.rb +31 -0
  25. data/lib/jade/codegen/pattern/constructor.rb +2 -2
  26. data/lib/jade/codegen/transforms/fold_shape.rb +1 -1
  27. data/lib/jade/codegen.rb +76 -15
  28. data/lib/jade/compiler.rb +2 -0
  29. data/lib/jade/entry.rb +1 -1
  30. data/lib/jade/error.rb +5 -0
  31. data/lib/jade/extensions.rb +70 -0
  32. data/lib/jade/formatter/calls.rb +68 -11
  33. data/lib/jade/formatter/collections.rb +4 -4
  34. data/lib/jade/formatter/declarations.rb +13 -15
  35. data/lib/jade/formatter/helper.rb +20 -0
  36. data/lib/jade/formatter/infix_application.rb +19 -4
  37. data/lib/jade/formatter/lambda.rb +24 -12
  38. data/lib/jade/formatter/pattern.rb +3 -0
  39. data/lib/jade/frontend/desugaring/placeholder.rb +40 -17
  40. data/lib/jade/frontend/desugaring.rb +29 -9
  41. data/lib/jade/frontend/fixity_fixer.rb +6 -4
  42. data/lib/jade/frontend/forward_declaration/error/alias_expansion.rb +23 -0
  43. data/lib/jade/frontend/forward_declaration/error/duplicate_constructor_name.rb +45 -0
  44. data/lib/jade/frontend/forward_declaration/error/duplicate_type_name.rb +42 -0
  45. data/lib/jade/frontend/forward_declaration/error.rb +3 -0
  46. data/lib/jade/frontend/forward_declaration/helper.rb +58 -0
  47. data/lib/jade/frontend/forward_declaration/import_declaration.rb +4 -0
  48. data/lib/jade/frontend/forward_declaration/interface_declaration.rb +6 -0
  49. data/lib/jade/frontend/forward_declaration/module.rb +4 -0
  50. data/lib/jade/frontend/forward_declaration/struct_declaration.rb +10 -1
  51. data/lib/jade/frontend/forward_declaration/type_alias_declaration.rb +37 -0
  52. data/lib/jade/frontend/forward_declaration/type_declaration.rb +10 -1
  53. data/lib/jade/frontend/forward_declaration.rb +2 -0
  54. data/lib/jade/frontend/pattern_analysis/exhaustiveness.rb +47 -43
  55. data/lib/jade/frontend/pattern_analysis/matrix.rb +28 -219
  56. data/lib/jade/frontend/pattern_analysis/pattern.rb +132 -0
  57. data/lib/jade/frontend/pattern_analysis/redundancy.rb +24 -0
  58. data/lib/jade/frontend/pattern_analysis/signature.rb +220 -0
  59. data/lib/jade/frontend/pattern_analysis/usefulness.rb +72 -0
  60. data/lib/jade/frontend/pattern_analysis/witnesses.rb +73 -0
  61. data/lib/jade/frontend/pattern_analysis.rb +5 -38
  62. data/lib/jade/frontend/semantic_analysis/body.rb +29 -1
  63. data/lib/jade/frontend/semantic_analysis/error/duplicate_implementation.rb +39 -0
  64. data/lib/jade/frontend/semantic_analysis/error/implementation_on_alias.rb +24 -0
  65. data/lib/jade/frontend/semantic_analysis/error/no_base_case.rb +45 -0
  66. data/lib/jade/frontend/semantic_analysis/error/placeholder_not_allowed.rb +15 -3
  67. data/lib/jade/frontend/semantic_analysis/error/recursive_type_alias.rb +24 -0
  68. data/lib/jade/frontend/semantic_analysis/error.rb +4 -0
  69. data/lib/jade/frontend/semantic_analysis/helper.rb +41 -0
  70. data/lib/jade/frontend/semantic_analysis/implementation.rb +13 -10
  71. data/lib/jade/frontend/semantic_analysis/inhabitedness.rb +90 -0
  72. data/lib/jade/frontend/semantic_analysis/interop_import_declaration.rb +28 -16
  73. data/lib/jade/frontend/semantic_analysis/keyed_call.rb +21 -5
  74. data/lib/jade/frontend/semantic_analysis/pattern_range.rb +16 -0
  75. data/lib/jade/frontend/semantic_analysis/struct_declaration.rb +1 -0
  76. data/lib/jade/frontend/semantic_analysis/type_alias_declaration/cycle_detection.rb +95 -0
  77. data/lib/jade/frontend/semantic_analysis/type_alias_declaration.rb +48 -0
  78. data/lib/jade/frontend/semantic_analysis/type_declaration.rb +1 -0
  79. data/lib/jade/frontend/semantic_analysis.rb +5 -0
  80. data/lib/jade/frontend/type_checking/canonicalize.rb +1 -0
  81. data/lib/jade/frontend/type_checking/cascade.rb +90 -0
  82. data/lib/jade/frontend/type_checking/constraints/deriving/encodable.rb +10 -0
  83. data/lib/jade/frontend/type_checking/constraints/deriving.rb +5 -4
  84. data/lib/jade/frontend/type_checking/constraints.rb +18 -3
  85. data/lib/jade/frontend/type_checking/error/display.rb +49 -0
  86. data/lib/jade/frontend/type_checking/error/division_by_zero.rb +31 -0
  87. data/lib/jade/frontend/type_checking/error/empty_range_pattern.rb +25 -0
  88. data/lib/jade/frontend/type_checking/error/function_body_type_mismatch.rb +4 -3
  89. data/lib/jade/frontend/type_checking/error/function_call_type_mismatch.rb +108 -5
  90. data/lib/jade/frontend/type_checking/error/if_branch_type_mismatch.rb +1 -1
  91. data/lib/jade/frontend/type_checking/error/if_branches_type_mismatch.rb +1 -1
  92. data/lib/jade/frontend/type_checking/error/if_condition_type_mismatch.rb +1 -1
  93. data/lib/jade/frontend/type_checking/error/implementation_type_mismatch.rb +1 -1
  94. data/lib/jade/frontend/type_checking/error/list_item_type_mismatch.rb +1 -1
  95. data/lib/jade/frontend/type_checking/error/missing_patterns.rb +20 -4
  96. data/lib/jade/frontend/type_checking/error/pattern_type_mismatch.rb +7 -1
  97. data/lib/jade/frontend/type_checking/error/range_pattern_type.rb +29 -0
  98. data/lib/jade/frontend/type_checking/error/record_access_type_mismatch.rb +2 -2
  99. data/lib/jade/frontend/type_checking/error/recursive_derivation.rb +25 -0
  100. data/lib/jade/frontend/type_checking/error/type_mismatch.rb +10 -2
  101. data/lib/jade/frontend/type_checking/error/unreachable_branch.rb +22 -0
  102. data/lib/jade/frontend/type_checking/error.rb +6 -0
  103. data/lib/jade/frontend/type_checking/inference/assign.rb +1 -1
  104. data/lib/jade/frontend/type_checking/inference/case_of.rb +21 -4
  105. data/lib/jade/frontend/type_checking/inference/division.rb +50 -0
  106. data/lib/jade/frontend/type_checking/inference/function_call.rb +47 -2
  107. data/lib/jade/frontend/type_checking/inference/lambda.rb +1 -1
  108. data/lib/jade/frontend/type_checking/inference/pattern.rb +35 -0
  109. data/lib/jade/frontend/type_checking/inference/type_alias_declaration.rb +18 -0
  110. data/lib/jade/frontend/type_checking/inference.rb +2 -0
  111. data/lib/jade/frontend/type_checking/loader.rb +11 -0
  112. data/lib/jade/frontend/type_checking/port_resolution.rb +26 -8
  113. data/lib/jade/frontend/type_checking/substitution.rb +2 -2
  114. data/lib/jade/frontend/type_checking.rb +3 -1
  115. data/lib/jade/frontend/usage_analysis.rb +5 -1
  116. data/lib/jade/interop/boundary.rb +64 -24
  117. data/lib/jade/interop/error.rb +35 -7
  118. data/lib/jade/interop/lowering.rb +8 -2
  119. data/lib/jade/lsp/converters.rb +83 -2
  120. data/lib/jade/lsp/handlers.rb +122 -25
  121. data/lib/jade/lsp/snippets.rb +12 -0
  122. data/lib/jade/lsp/state.rb +11 -4
  123. data/lib/jade/module_loader/build.rb +36 -0
  124. data/lib/jade/module_loader.rb +1 -0
  125. data/lib/jade/parsing/combinators.rb +78 -2
  126. data/lib/jade/parsing/error.rb +43 -6
  127. data/lib/jade/parsing/type.rb +5 -3
  128. data/lib/jade/parsing.rb +140 -26
  129. data/lib/jade/runtime.rb +32 -13
  130. data/lib/jade/source.rb +6 -2
  131. data/lib/jade/stdlib/basics.rb +22 -3
  132. data/lib/jade/stdlib/char.rb +2 -0
  133. data/lib/jade/stdlib/clock.rb +6 -2
  134. data/lib/jade/stdlib/decimal.rb +51 -14
  135. data/lib/jade/stdlib/intrinsics.rb +59 -6
  136. data/lib/jade/stdlib/list.rb +37 -1
  137. data/lib/jade/stdlib/number.rb +47 -0
  138. data/lib/jade/stdlib/range.rb +211 -0
  139. data/lib/jade/stdlib.rb +11 -4
  140. data/lib/jade/symbol/alias.rb +11 -0
  141. data/lib/jade/symbol/parser.rb +1 -1
  142. data/lib/jade/symbol.rb +13 -1
  143. data/lib/jade/task.rb +74 -56
  144. data/lib/jade/type/anonymous_record.rb +11 -2
  145. data/lib/jade/type/application.rb +11 -2
  146. data/lib/jade/type/base.rb +26 -0
  147. data/lib/jade/type/function.rb +11 -2
  148. data/lib/jade/type.rb +36 -10
  149. data/lib/jade/version.rb +1 -1
  150. data/lib/jade.rb +1 -0
  151. metadata +35 -3
  152. data/lib/jade/frontend/type_checking/constraints/deriving/assignable.rb +0 -170
data/lib/jade/parsing.rb CHANGED
@@ -92,7 +92,7 @@ module Jade
92
92
  parser(:program) { module_ | program_body }
93
93
 
94
94
  parser(:program_body) {
95
- (declaration | statement).then { |item|
95
+ (declaration | statement).expecting('a declaration or an expression').then { |item|
96
96
  P.new do |state|
97
97
  (
98
98
  state.tolerant ?
@@ -105,11 +105,11 @@ module Jade
105
105
  }
106
106
  }
107
107
 
108
- parser(:statement) { bind | assign | expression }
108
+ parser(:statement) { (bind | assign | expression).expecting('an expression') }
109
109
 
110
110
  parser(:declaration) {
111
- function_declaration | type_declaration | import_declaration | interop_import_declaration |
112
- struct_declaration | implementation | interface_declaration
111
+ function_declaration | type_alias_declaration | type_declaration | import_declaration |
112
+ interop_import_declaration | struct_declaration | implementation | interface_declaration
113
113
  }
114
114
 
115
115
  parser(:expression) {
@@ -129,15 +129,15 @@ module Jade
129
129
  type(:colon).skip >>
130
130
  lazy { expression }
131
131
  ).commit
132
- .context("ternary")
132
+ .parsing("ternary")
133
133
  }
134
134
 
135
135
  parser(:tuple) {
136
136
  (
137
137
  type(:lparen) >>
138
- lazy { expression } >>
138
+ function_call_arg >>
139
139
  type(:comma).skip >>
140
- comma_sequence(lazy { expression }) >>
140
+ comma_sequence(function_call_arg) >>
141
141
  type(:rparen)
142
142
  ).map(&AST.tuple)
143
143
  }
@@ -182,7 +182,8 @@ module Jade
182
182
  type(:end)
183
183
  ).commit
184
184
  ).map(&AST.if_then_else)
185
- .context("if/then/else")
185
+ .parsing("if/then/else")
186
+ .spanning_empty_body
186
187
  }
187
188
 
188
189
  parser(:case_of) {
@@ -195,7 +196,8 @@ module Jade
195
196
  type(:end)
196
197
  ).commit
197
198
  ).map(&AST.case_of)
198
- .context("case")
199
+ .parsing("case")
200
+ .spanning_empty_body
199
201
  }
200
202
 
201
203
  # `then` is optional before a branch body. The formatter emits it for
@@ -233,6 +235,21 @@ module Jade
233
235
  state.current&.type == :then ? body.call(state.advance) : body.call(state)
234
236
  end
235
237
 
238
+ def empty_body?(state)
239
+ state.position.positive? &&
240
+ !state.eof? &&
241
+ BODY_TERMINATORS.include?(state.current.type)
242
+ end
243
+
244
+ # Widened to the whole construct by `spanning_empty_body`.
245
+ def empty_body_start(state)
246
+ state
247
+ .tokens[state.position - 1]
248
+ .range
249
+ .end
250
+ .then { it...(it + 1) }
251
+ end
252
+
236
253
  def checked_branch_body(in_tok)
237
254
  P.new do |state|
238
255
  next body.call(state.advance) if state.current&.type == :then
@@ -256,10 +273,72 @@ module Jade
256
273
  end
257
274
 
258
275
  parser(:pattern) {
259
- wildcard_pattern | list_pattern | literal_pattern | binding_pattern |
260
- constructor_pattern | tuple_pattern | record_pattern
276
+ wildcard_pattern | list_pattern | range_pattern | literal_pattern |
277
+ binding_pattern | constructor_pattern | tuple_pattern | record_pattern
278
+ }
279
+
280
+ # Tried ahead of `literal_pattern`, which would otherwise take the lower
281
+ # bound and leave the `..` stranded.
282
+ parser(:range_pattern) {
283
+ ((range_bound >> type(:dotdot) >> upper_bound).map(&AST.range_pattern)) |
284
+ ((type(:dotdot) >> range_bound).map(&AST.unbounded_below_pattern)) |
285
+ non_literal_lower_bound
261
286
  }
262
287
 
288
+ parser(:non_literal_lower_bound, private: true) {
289
+ P.new do |state|
290
+ bound = state.current
291
+
292
+ next not_a_range(state) unless bound&.type == :identifier
293
+ next not_a_range(state) unless state.advance.current&.type == :dotdot
294
+
295
+ Err[[non_literal_bound(state, bound), state]]
296
+ end
297
+ }
298
+
299
+ parser(:range_bound, private: true) {
300
+ negative_literal | int | float | char | string
301
+ }
302
+
303
+ # A branch whose body is on the next line has no `then` to stop at, so an
304
+ # open range would otherwise take the first line of the body as its upper
305
+ # bound.
306
+ parser(:upper_bound, private: true) {
307
+ P.new do |state|
308
+ next Ok[[nil, state]] if state.eof?
309
+ next Ok[[nil, state]] unless state.same_line?(
310
+ state.tokens[state.position - 1].range.begin,
311
+ state.current.range.begin,
312
+ )
313
+
314
+ bound = state.current
315
+ next Err[[non_literal_bound(state, bound), state]] if bound.type == :identifier
316
+
317
+ optional(range_bound).call(state)
318
+ end
319
+ }
320
+
321
+ def not_a_range(state)
322
+ UnexpectedTokenError
323
+ .new(
324
+ entry: state.entry,
325
+ span: state.current&.range || (0...0),
326
+ actual: state.current,
327
+ expected: :int,
328
+ )
329
+ .then { Err[[it, state]] }
330
+ end
331
+
332
+ def non_literal_bound(state, token)
333
+ NonLiteralRangeBoundError.new(
334
+ entry: state.entry,
335
+ span: token.range,
336
+ actual: token,
337
+ expected: :int,
338
+ committed: true,
339
+ )
340
+ end
341
+
263
342
  parser(:tuple_pattern) {
264
343
  (
265
344
  type(:lparen) >>
@@ -308,13 +387,31 @@ module Jade
308
387
  ).map(&AST.record_field_pattern)
309
388
  }
310
389
 
311
- parser(:body) { sequence(lazy { statement }).map(&AST.body) }
390
+ # One of these with nothing before it is an empty body.
391
+ BODY_TERMINATORS = %i[end else rbrace in].freeze
392
+
393
+ parser(:body) {
394
+ P.new do |state|
395
+ next sequence(lazy { statement }).map(&AST.body).call(state) unless empty_body?(state)
396
+
397
+ Err[[
398
+ MissingBodyError.new(
399
+ entry: state.entry,
400
+ span: empty_body_start(state),
401
+ actual: state.current,
402
+ expected: 'an expression',
403
+ committed: true,
404
+ ),
405
+ state,
406
+ ]]
407
+ end
408
+ }
312
409
 
313
410
  parser(:operator) {
314
411
  type(:plus) | type(:minus) | type(:star) | type(:slash) |
315
412
  type(:pipe_forward) | type(:pipe_backward) | type(:eq) | type(:not_eq) |
316
413
  type(:lt) | type(:gt) | type(:lte) | type(:gte) | type(:andand) | type(:oror) |
317
- type(:plusplus)
414
+ type(:plusplus) | type(:dotdot)
318
415
  }
319
416
 
320
417
  parser(:primary) {
@@ -342,7 +439,7 @@ module Jade
342
439
  record_literal | record_update_sugar | record_access_sugar | record_update
343
440
  }
344
441
 
345
- parser(:postfix) { function_call | member_access }
442
+ parser(:postfix) { function_call.on_same_line | member_access }
346
443
 
347
444
  parser(:module_name) {
348
445
  sequence(constant, separated_by: type(:dot).skip).map { [it] }
@@ -394,14 +491,16 @@ module Jade
394
491
  optional(exposing, default: [[]])
395
492
  ).commit
396
493
  ).map(&AST.import_declaration)
397
- .context("import declaration")
494
+ .parsing("import declaration")
398
495
  }
399
496
 
400
497
  parser(:exposing) {
401
498
  (type(:exposing).skip >>
402
- type(:lparen).skip >>
403
- (expose_list | expose_all) >>
404
- type(:rparen).skip
499
+ (
500
+ type(:lparen).skip >>
501
+ (expose_list | expose_all).expecting('a name to expose, or `..`') >>
502
+ type(:rparen).skip
503
+ ).commit
405
504
  ) | expose_none
406
505
  }
407
506
 
@@ -437,11 +536,12 @@ module Jade
437
536
  ) >>
438
537
  type(:arrow).skip >>
439
538
  type_expression >>
440
- sequence(statement).map(&AST.body) >>
539
+ body >>
441
540
  type(:end)
442
541
  ).commit
443
542
  ).map(&AST.function_declaration)
444
- .context("function declaration")
543
+ .parsing("function declaration")
544
+ .spanning_empty_body
445
545
  }
446
546
 
447
547
  parser(:type_declaration) {
@@ -454,7 +554,21 @@ module Jade
454
554
  sequence(variant_declaration, separated_by: type(:pipe).skip).map { [it] }
455
555
  ).commit
456
556
  ).map(&AST.type_declaration)
457
- .context("type declaration")
557
+ .parsing("type declaration")
558
+ }
559
+
560
+ parser(:type_alias_declaration) {
561
+ (
562
+ type(:type) >>
563
+ contextual('alias').skip >>
564
+ (
565
+ constant >>
566
+ (type_params | empty_comma_list) >>
567
+ type(:assign).skip >>
568
+ type_expression
569
+ ).commit
570
+ ).map(&AST.type_alias_declaration)
571
+ .parsing("type alias declaration")
458
572
  }
459
573
 
460
574
  parser(:record_declaration) {
@@ -549,7 +663,7 @@ module Jade
549
663
  parser(:record_fields) { comma_sequence(record_field) }
550
664
 
551
665
  parser(:record_field) {
552
- (identifier >> type(:colon).skip >> lazy { expression }).map(&AST.record_field)
666
+ (identifier >> type(:colon).skip >> function_call_arg).map(&AST.record_field)
553
667
  }
554
668
 
555
669
  parser(:record_access_sugar) {
@@ -571,7 +685,7 @@ module Jade
571
685
  (interop_module_name >> type(:with) >> interop_functions >> type(:end))
572
686
  .commit
573
687
  ).map(&AST.interop_import_declaration)
574
- .context("interop import declaration")
688
+ .parsing("interop import declaration")
575
689
  }
576
690
 
577
691
  parser(:interop_namespace_sep) { type(:coloncolon) }
@@ -615,7 +729,7 @@ module Jade
615
729
  type(:end)
616
730
  ).commit
617
731
  ).map(&AST.implementation)
618
- .context("implementation")
732
+ .parsing("implementation")
619
733
  }
620
734
 
621
735
  parser(:interface_declaration) {
@@ -635,7 +749,7 @@ module Jade
635
749
  type(:end)
636
750
  ).commit
637
751
  ).map(&AST.interface_declaration)
638
- .context("interface declaration")
752
+ .parsing("interface declaration")
639
753
  }
640
754
 
641
755
  parser(:interface_function_decl) {
@@ -665,7 +779,7 @@ module Jade
665
779
  type_record
666
780
  ).commit
667
781
  ).map(&AST.struct_declaration)
668
- .context("struct declaration")
782
+ .parsing("struct declaration")
669
783
  }
670
784
 
671
785
  parser(:int) { type(:int).map(&AST.literal) }
data/lib/jade/runtime.rb CHANGED
@@ -8,6 +8,25 @@ require 'jade/interop/boundary'
8
8
  module Jade
9
9
  module Records; end
10
10
 
11
+ # A variant with no fields carries no state, so every construction can
12
+ # hand back the same object. Prepended to the class's singleton so
13
+ # `super` still reaches the real constructor the first time.
14
+ module Nullary
15
+ def new
16
+ @instance ||= super
17
+ end
18
+
19
+ def []
20
+ @instance ||= super
21
+ end
22
+ end
23
+
24
+ def self.nullary(&block)
25
+ Data.define
26
+ .tap { it.class_eval(&block) if block }
27
+ .tap { it.singleton_class.prepend(Nullary) }
28
+ end
29
+
11
30
  module Tuple
12
31
  Tuple2 = Data.define(:_1, :_2) do
13
32
  def to_s = "(#{[_1, _2].map(&:to_s).join(', ')})"
@@ -23,9 +42,9 @@ module Jade
23
42
  end
24
43
 
25
44
  module Basics
26
- GT = Data.define()
27
- EQ = Data.define()
28
- LT = Data.define()
45
+ GT = Jade.nullary
46
+ EQ = Jade.nullary
47
+ LT = Jade.nullary
29
48
  end
30
49
 
31
50
  # Compiled code calls `a.compare(b)`; natives ship `<=>` not `compare`.
@@ -77,20 +96,20 @@ module Jade
77
96
  RECORD_CLASSES = {}
78
97
  @booted = false
79
98
 
99
+ # In dependency order: a module's `import` names the constant of another.
100
+ STDLIB = %w[
101
+ basics maybe number tuple list char string result task dict set
102
+ decode decode/params encode bytes show debug
103
+ ].freeze
104
+
80
105
  def boot!
81
106
  return if @booted
82
107
  @booted = true
83
108
 
84
- require "jade/stdlib/basics"
85
- require "jade/stdlib/string"
86
- require "jade/stdlib/list"
87
- require "jade/stdlib/tuple"
88
- require "jade/stdlib/task"
89
- require "jade/stdlib/decode"
90
- require "jade/stdlib/encode"
91
- require "jade/stdlib/bytes"
92
- require "jade/stdlib/dict"
93
- require "jade/stdlib/set"
109
+ require 'jade/stdlib/intrinsics'
110
+ Stdlib::Intrinsics.runtime_only! unless defined?(Jade::Frontend)
111
+
112
+ STDLIB.each { require_relative "stdlib/#{it}" }
94
113
  end
95
114
 
96
115
  def intr(name)
data/lib/jade/source.rb CHANGED
@@ -35,10 +35,14 @@ module Jade
35
35
  end
36
36
 
37
37
  def to_module_name
38
- uri
38
+ Source.module_name_for(uri)
39
+ end
40
+
41
+ def self.module_name_for(path)
42
+ path
39
43
  .delete_suffix('.jd')
40
44
  .split('/')
41
- .map { Source.camelize(it) }
45
+ .map { camelize(it) }
42
46
  .join('.')
43
47
  end
44
48
 
@@ -14,6 +14,10 @@ module Jade
14
14
  native_type :Float, ::Float
15
15
  native_type :Bool, TrueClass, FalseClass
16
16
 
17
+ # No variants, so the only way to hold one is `non_zero`. Erased at
18
+ # runtime: a NonZero(Int) is the Int.
19
+ union :NonZero, 'a'
20
+
17
21
  union :Ordering
18
22
  variant :GT, of: :Ordering
19
23
  variant :EQ, of: :Ordering
@@ -41,11 +45,25 @@ module Jade
41
45
  interface(
42
46
  'Numeric',
43
47
  'a',
44
- { '(+)' => 'a, a -> a', '(-)' => 'a, a -> a', '(*)' => 'a, a -> a', '(/)' => 'a, a -> a' }
48
+ {
49
+ '(+)' => 'a, a -> a',
50
+ '(-)' => 'a, a -> a',
51
+ '(*)' => 'a, a -> a',
52
+ '(/)' => 'a, NonZero(a) -> a',
53
+ 'from_int' => 'Int -> a',
54
+ }
45
55
  )
46
56
 
47
- implementation('Numeric', 'Int', '(+)' => 'int_add', '(-)' => 'int_sub', '(*)' => 'int_mul', '(/)' => 'int_div')
48
- implementation('Numeric', 'Float', '(+)' => 'float_add', '(-)' => 'float_sub', '(*)' => 'float_mul', '(/)' => 'float_div')
57
+ implementation(
58
+ 'Numeric', 'Int',
59
+ '(+)' => 'int_add', '(-)' => 'int_sub', '(*)' => 'int_mul', '(/)' => 'int_div',
60
+ 'from_int' => 'identity'
61
+ )
62
+ implementation(
63
+ 'Numeric', 'Float',
64
+ '(+)' => 'float_add', '(-)' => 'float_sub', '(*)' => 'float_mul', '(/)' => 'float_div',
65
+ 'from_int' => 'to_float'
66
+ )
49
67
 
50
68
  interface(
51
69
  'Appendable',
@@ -82,6 +100,7 @@ module Jade
82
100
  function('int_div', { a: 'Int', b: 'Int' }, 'Int')
83
101
  function('mod', { a: 'Int', b: 'Int' }, 'Int')
84
102
 
103
+
85
104
  function('float_add', { a: 'Float', b: 'Float' }, 'Float')
86
105
  function('float_sub', { a: 'Float', b: 'Float' }, 'Float')
87
106
  function('float_mul', { a: 'Float', b: 'Float' }, 'Float')
@@ -10,6 +10,7 @@ module Jade
10
10
  union :Char
11
11
 
12
12
  implementation('Eq', 'Char', '(==)' => 'char_eq')
13
+ implementation('Comparable', 'Char', 'compare' => 'char_compare')
13
14
 
14
15
  function(:to_code, { char: 'Char' }, 'Int')
15
16
  function(:from_code, { code: 'Int' }, 'Maybe(Char)')
@@ -22,6 +23,7 @@ module Jade
22
23
  default_importing('Char')
23
24
 
24
25
  function('char_eq', { one: 'Char', other: 'Char' }, 'Bool')
26
+ function('char_compare', { one: 'Char', other: 'Char' }, 'Ordering')
25
27
  end
26
28
  end
27
29
  end
@@ -11,7 +11,7 @@ module Jade
11
11
  end
12
12
 
13
13
  def imports
14
- [Basics, Maybe, Result, Task, String, Tuple, Decode, Encode, Calendar]
14
+ [Basics, Maybe, Number, Result, Task, String, Tuple, Decode, Encode, Calendar]
15
15
  end
16
16
 
17
17
  def default_imports
@@ -46,6 +46,7 @@ module Jade
46
46
 
47
47
  import Decode exposing (Decodable, Decoder, Value)
48
48
  import Encode exposing (Encodable)
49
+ import Number exposing (non_zero)
49
50
 
50
51
 
51
52
  type Instant = Instant(Int)
@@ -259,7 +260,10 @@ module Jade
259
260
 
260
261
 
261
262
  def frac_to_millis(n: Int, digits: Int) -> Int
262
- digits == 3 ? n : digits < 3 ? n * pow10(3 - digits) : n / pow10(digits - 3)
263
+ case non_zero(pow10(digits - 3))
264
+ in Just(d) then digits == 3 ? n : digits < 3 ? n * pow10(3 - digits) : n / d
265
+ in Nothing then n
266
+ end
263
267
  end
264
268
 
265
269
 
@@ -36,6 +36,7 @@ module Jade
36
36
  import Maybe
37
37
  import Decode exposing (Decodable, Decoder)
38
38
  import Encode exposing (Encodable)
39
+ import Number exposing (non_zero)
39
40
 
40
41
 
41
42
  # An exact base-10 decimal: value = coefficient * 10 ^ exponent.
@@ -81,24 +82,45 @@ module Jade
81
82
 
82
83
 
83
84
  # Integer division, rounding ties half away from zero (Java HALF_UP /
84
- # Ruby BigDecimal default: 2.5 -> 3, -2.5 -> -3). A zero `den` raises
85
- # through Int `/`.
85
+ # Ruby BigDecimal default: 2.5 -> 3, -2.5 -> -3).
86
86
  def round_div(num: Int, den: Int) -> Int
87
87
  negative = not ((num < 0) == (den < 0))
88
- n = abs(num)
89
- d = abs(den)
90
- q = n / d
88
+ magnitude = round_div_abs(abs(num), abs(den))
89
+
90
+ negative ? 0 - magnitude : magnitude
91
+ end
92
+
93
+
94
+ # Every caller divides by a scale or by a checked divisor, so the
95
+ # Nothing arm is unreachable rather than a rounding decision.
96
+ def round_div_abs(n: Int, d: Int) -> Int
97
+ case non_zero(d)
98
+ in Just(nz) then rounded_quotient(n, nz, d)
99
+ in Nothing then 0
100
+ end
101
+ end
102
+
103
+
104
+ def rounded_quotient(n: Int, nz: NonZero(Int), d: Int) -> Int
105
+ q = n / nz
91
106
  r = n - q * d
92
- rounded = (2 * r) >= d ? q + 1 : q
93
107
 
94
- negative ? 0 - rounded : rounded
108
+ (2 * r) >= d ? q + 1 : q
95
109
  end
96
110
 
97
111
 
98
112
  def trunc_div(num: Int, den: Int) -> Int
99
- q = abs(num) / abs(den)
113
+ magnitude = trunc_div_abs(abs(num), abs(den))
114
+
115
+ num < 0 ? 0 - magnitude : magnitude
116
+ end
100
117
 
101
- num < 0 ? 0 - q : q
118
+
119
+ def trunc_div_abs(n: Int, d: Int) -> Int
120
+ case non_zero(d)
121
+ in Just(nz) then n / nz
122
+ in Nothing then 0
123
+ end
102
124
  end
103
125
 
104
126
 
@@ -132,9 +154,9 @@ module Jade
132
154
 
133
155
  # a / b rounded half-up to `scale` decimal places, like Java's
134
156
  # BigDecimal.divide(divisor, scale, HALF_UP). Division by zero raises.
135
- def div(a: Decimal, b: Decimal, scale: Int) -> Decimal
157
+ def div(a: Decimal, b: NonZero(Decimal), scale: Int) -> Decimal
136
158
  Decimal(ma, ea) = a
137
- Decimal(mb, eb) = b
159
+ Decimal(mb, eb) = Number.unwrap(b)
138
160
  k = (ea - eb) + scale
139
161
  num = k >= 0 ? ma * pow10(k) : ma
140
162
  den = k >= 0 ? mb : mb * pow10(0 - k)
@@ -145,16 +167,22 @@ module Jade
145
167
 
146
168
  # The Numeric `/`: like Ruby's BigDecimal `/`, it can't be exact for a
147
169
  # repeating quotient (1/3), so it rounds half-up to a generous scale.
148
- def divide(a: Decimal, b: Decimal) -> Decimal
170
+ def divide(a: Decimal, b: NonZero(Decimal)) -> Decimal
149
171
  div(a, b, 32)
150
172
  end
151
173
 
152
174
 
175
+ def from_int(n: Int) -> Decimal
176
+ Decimal(n, 0)
177
+ end
178
+
179
+
153
180
  implements Numeric(Decimal) with
154
181
  (+): add,
155
182
  (-): sub,
156
183
  (*): mul,
157
- (/): divide
184
+ (/): divide,
185
+ from_int: from_int
158
186
  end
159
187
 
160
188
 
@@ -181,7 +209,16 @@ module Jade
181
209
 
182
210
  e >= 0
183
211
  ? Basics.to_float(m * pow10(e))
184
- : Basics.to_float(m) / Basics.to_float(pow10(0 - e))
212
+ : scaled_down(Basics.to_float(m), Basics.to_float(pow10(0 - e)))
213
+ end
214
+
215
+
216
+ # `pow10` is never zero, so the Nothing arm cannot be reached.
217
+ def scaled_down(m: Float, scale: Float) -> Float
218
+ case non_zero(scale)
219
+ in Just(nz) then m / nz
220
+ in Nothing then m
221
+ end
185
222
  end
186
223
 
187
224