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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +439 -2
- data/docs/interop.md +18 -1
- data/docs/stdlib.md +3 -1
- data/docs/syntax.md +96 -0
- data/lib/jade/api.rb +9 -0
- data/lib/jade/ast.rb +37 -1
- data/lib/jade/cli/eject.rb +132 -0
- data/lib/jade/cli.rb +2 -0
- data/lib/jade/codegen/boundary/specialized/list.rb +11 -7
- data/lib/jade/codegen/boundary/specialized/maybe.rb +2 -2
- data/lib/jade/codegen/boundary/specialized/record.rb +19 -4
- data/lib/jade/codegen/boundary/specialized/scalar.rb +2 -2
- data/lib/jade/codegen/boundary/specialized.rb +13 -5
- data/lib/jade/codegen/boundary.rb +12 -2
- data/lib/jade/codegen/context.rb +17 -0
- data/lib/jade/codegen/error.rb +1 -0
- data/lib/jade/codegen/function_call.rb +23 -7
- data/lib/jade/codegen/function_declaration.rb +25 -13
- data/lib/jade/codegen/helpers.rb +27 -4
- data/lib/jade/codegen/implementation.rb +2 -2
- data/lib/jade/codegen/inline.rb +1 -1
- data/lib/jade/codegen/inlines.rb +6 -1
- data/lib/jade/codegen/names.rb +31 -0
- data/lib/jade/codegen/pattern/constructor.rb +2 -2
- data/lib/jade/codegen/transforms/fold_shape.rb +1 -1
- data/lib/jade/codegen.rb +76 -15
- data/lib/jade/compiler.rb +2 -0
- data/lib/jade/entry.rb +1 -1
- data/lib/jade/error.rb +5 -0
- data/lib/jade/extensions.rb +70 -0
- data/lib/jade/formatter/calls.rb +68 -11
- data/lib/jade/formatter/collections.rb +4 -4
- data/lib/jade/formatter/declarations.rb +13 -15
- data/lib/jade/formatter/helper.rb +20 -0
- data/lib/jade/formatter/infix_application.rb +19 -4
- data/lib/jade/formatter/lambda.rb +24 -12
- data/lib/jade/formatter/pattern.rb +3 -0
- data/lib/jade/frontend/desugaring/placeholder.rb +40 -17
- data/lib/jade/frontend/desugaring.rb +29 -9
- data/lib/jade/frontend/fixity_fixer.rb +6 -4
- data/lib/jade/frontend/forward_declaration/error/alias_expansion.rb +23 -0
- data/lib/jade/frontend/forward_declaration/error/duplicate_constructor_name.rb +45 -0
- data/lib/jade/frontend/forward_declaration/error/duplicate_type_name.rb +42 -0
- data/lib/jade/frontend/forward_declaration/error.rb +3 -0
- data/lib/jade/frontend/forward_declaration/helper.rb +58 -0
- data/lib/jade/frontend/forward_declaration/import_declaration.rb +4 -0
- data/lib/jade/frontend/forward_declaration/interface_declaration.rb +6 -0
- data/lib/jade/frontend/forward_declaration/module.rb +4 -0
- data/lib/jade/frontend/forward_declaration/struct_declaration.rb +10 -1
- data/lib/jade/frontend/forward_declaration/type_alias_declaration.rb +37 -0
- data/lib/jade/frontend/forward_declaration/type_declaration.rb +10 -1
- data/lib/jade/frontend/forward_declaration.rb +2 -0
- data/lib/jade/frontend/pattern_analysis/exhaustiveness.rb +47 -43
- data/lib/jade/frontend/pattern_analysis/matrix.rb +28 -219
- data/lib/jade/frontend/pattern_analysis/pattern.rb +132 -0
- data/lib/jade/frontend/pattern_analysis/redundancy.rb +24 -0
- data/lib/jade/frontend/pattern_analysis/signature.rb +220 -0
- data/lib/jade/frontend/pattern_analysis/usefulness.rb +72 -0
- data/lib/jade/frontend/pattern_analysis/witnesses.rb +73 -0
- data/lib/jade/frontend/pattern_analysis.rb +5 -38
- data/lib/jade/frontend/semantic_analysis/body.rb +29 -1
- data/lib/jade/frontend/semantic_analysis/error/duplicate_implementation.rb +39 -0
- data/lib/jade/frontend/semantic_analysis/error/implementation_on_alias.rb +24 -0
- data/lib/jade/frontend/semantic_analysis/error/no_base_case.rb +45 -0
- data/lib/jade/frontend/semantic_analysis/error/placeholder_not_allowed.rb +15 -3
- data/lib/jade/frontend/semantic_analysis/error/recursive_type_alias.rb +24 -0
- data/lib/jade/frontend/semantic_analysis/error.rb +4 -0
- data/lib/jade/frontend/semantic_analysis/helper.rb +41 -0
- data/lib/jade/frontend/semantic_analysis/implementation.rb +13 -10
- data/lib/jade/frontend/semantic_analysis/inhabitedness.rb +90 -0
- data/lib/jade/frontend/semantic_analysis/interop_import_declaration.rb +28 -16
- data/lib/jade/frontend/semantic_analysis/keyed_call.rb +21 -5
- data/lib/jade/frontend/semantic_analysis/pattern_range.rb +16 -0
- data/lib/jade/frontend/semantic_analysis/struct_declaration.rb +1 -0
- data/lib/jade/frontend/semantic_analysis/type_alias_declaration/cycle_detection.rb +95 -0
- data/lib/jade/frontend/semantic_analysis/type_alias_declaration.rb +48 -0
- data/lib/jade/frontend/semantic_analysis/type_declaration.rb +1 -0
- data/lib/jade/frontend/semantic_analysis.rb +5 -0
- data/lib/jade/frontend/type_checking/canonicalize.rb +1 -0
- data/lib/jade/frontend/type_checking/cascade.rb +90 -0
- data/lib/jade/frontend/type_checking/constraints/deriving/encodable.rb +10 -0
- data/lib/jade/frontend/type_checking/constraints/deriving.rb +5 -4
- data/lib/jade/frontend/type_checking/constraints.rb +18 -3
- data/lib/jade/frontend/type_checking/error/display.rb +49 -0
- data/lib/jade/frontend/type_checking/error/division_by_zero.rb +31 -0
- data/lib/jade/frontend/type_checking/error/empty_range_pattern.rb +25 -0
- data/lib/jade/frontend/type_checking/error/function_body_type_mismatch.rb +4 -3
- data/lib/jade/frontend/type_checking/error/function_call_type_mismatch.rb +108 -5
- data/lib/jade/frontend/type_checking/error/if_branch_type_mismatch.rb +1 -1
- data/lib/jade/frontend/type_checking/error/if_branches_type_mismatch.rb +1 -1
- data/lib/jade/frontend/type_checking/error/if_condition_type_mismatch.rb +1 -1
- data/lib/jade/frontend/type_checking/error/implementation_type_mismatch.rb +1 -1
- data/lib/jade/frontend/type_checking/error/list_item_type_mismatch.rb +1 -1
- data/lib/jade/frontend/type_checking/error/missing_patterns.rb +20 -4
- data/lib/jade/frontend/type_checking/error/pattern_type_mismatch.rb +7 -1
- data/lib/jade/frontend/type_checking/error/range_pattern_type.rb +29 -0
- data/lib/jade/frontend/type_checking/error/record_access_type_mismatch.rb +2 -2
- data/lib/jade/frontend/type_checking/error/recursive_derivation.rb +25 -0
- data/lib/jade/frontend/type_checking/error/type_mismatch.rb +10 -2
- data/lib/jade/frontend/type_checking/error/unreachable_branch.rb +22 -0
- data/lib/jade/frontend/type_checking/error.rb +6 -0
- data/lib/jade/frontend/type_checking/inference/assign.rb +1 -1
- data/lib/jade/frontend/type_checking/inference/case_of.rb +21 -4
- data/lib/jade/frontend/type_checking/inference/division.rb +50 -0
- data/lib/jade/frontend/type_checking/inference/function_call.rb +47 -2
- data/lib/jade/frontend/type_checking/inference/lambda.rb +1 -1
- data/lib/jade/frontend/type_checking/inference/pattern.rb +35 -0
- data/lib/jade/frontend/type_checking/inference/type_alias_declaration.rb +18 -0
- data/lib/jade/frontend/type_checking/inference.rb +2 -0
- data/lib/jade/frontend/type_checking/loader.rb +11 -0
- data/lib/jade/frontend/type_checking/port_resolution.rb +26 -8
- data/lib/jade/frontend/type_checking/substitution.rb +2 -2
- data/lib/jade/frontend/type_checking.rb +3 -1
- data/lib/jade/frontend/usage_analysis.rb +5 -1
- data/lib/jade/interop/boundary.rb +64 -24
- data/lib/jade/interop/error.rb +35 -7
- data/lib/jade/interop/lowering.rb +8 -2
- data/lib/jade/lsp/converters.rb +83 -2
- data/lib/jade/lsp/handlers.rb +122 -25
- data/lib/jade/lsp/snippets.rb +12 -0
- data/lib/jade/lsp/state.rb +11 -4
- data/lib/jade/module_loader/build.rb +36 -0
- data/lib/jade/module_loader.rb +1 -0
- data/lib/jade/parsing/combinators.rb +78 -2
- data/lib/jade/parsing/error.rb +43 -6
- data/lib/jade/parsing/type.rb +5 -3
- data/lib/jade/parsing.rb +140 -26
- data/lib/jade/runtime.rb +32 -13
- data/lib/jade/source.rb +6 -2
- data/lib/jade/stdlib/basics.rb +22 -3
- data/lib/jade/stdlib/char.rb +2 -0
- data/lib/jade/stdlib/clock.rb +6 -2
- data/lib/jade/stdlib/decimal.rb +51 -14
- data/lib/jade/stdlib/intrinsics.rb +59 -6
- data/lib/jade/stdlib/list.rb +37 -1
- data/lib/jade/stdlib/number.rb +47 -0
- data/lib/jade/stdlib/range.rb +211 -0
- data/lib/jade/stdlib.rb +11 -4
- data/lib/jade/symbol/alias.rb +11 -0
- data/lib/jade/symbol/parser.rb +1 -1
- data/lib/jade/symbol.rb +13 -1
- data/lib/jade/task.rb +74 -56
- data/lib/jade/type/anonymous_record.rb +11 -2
- data/lib/jade/type/application.rb +11 -2
- data/lib/jade/type/base.rb +26 -0
- data/lib/jade/type/function.rb +11 -2
- data/lib/jade/type.rb +36 -10
- data/lib/jade/version.rb +1 -1
- data/lib/jade.rb +1 -0
- metadata +35 -3
- 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 |
|
|
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
|
-
.
|
|
132
|
+
.parsing("ternary")
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
parser(:tuple) {
|
|
136
136
|
(
|
|
137
137
|
type(:lparen) >>
|
|
138
|
-
|
|
138
|
+
function_call_arg >>
|
|
139
139
|
type(:comma).skip >>
|
|
140
|
-
comma_sequence(
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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 |
|
|
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
|
-
|
|
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
|
-
.
|
|
494
|
+
.parsing("import declaration")
|
|
398
495
|
}
|
|
399
496
|
|
|
400
497
|
parser(:exposing) {
|
|
401
498
|
(type(:exposing).skip >>
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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
|
-
|
|
539
|
+
body >>
|
|
441
540
|
type(:end)
|
|
442
541
|
).commit
|
|
443
542
|
).map(&AST.function_declaration)
|
|
444
|
-
.
|
|
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
|
-
.
|
|
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 >>
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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 =
|
|
27
|
-
EQ =
|
|
28
|
-
LT =
|
|
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
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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 {
|
|
45
|
+
.map { camelize(it) }
|
|
42
46
|
.join('.')
|
|
43
47
|
end
|
|
44
48
|
|
data/lib/jade/stdlib/basics.rb
CHANGED
|
@@ -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
|
-
{
|
|
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(
|
|
48
|
-
|
|
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')
|
data/lib/jade/stdlib/char.rb
CHANGED
|
@@ -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
|
data/lib/jade/stdlib/clock.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
|
data/lib/jade/stdlib/decimal.rb
CHANGED
|
@@ -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).
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
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
|
-
|
|
113
|
+
magnitude = trunc_div_abs(abs(num), abs(den))
|
|
114
|
+
|
|
115
|
+
num < 0 ? 0 - magnitude : magnitude
|
|
116
|
+
end
|
|
100
117
|
|
|
101
|
-
|
|
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)
|
|
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
|
|