dhall 0.3.0 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,7 +12,7 @@ module Dhall
12
12
 
13
13
  def self.assert_type(expr, assertion, message, context:)
14
14
  aexpr = self.for(expr).annotate(context)
15
- type = aexpr.type
15
+ type = aexpr.type.normalize
16
16
  raise TypeError, "#{message}: #{type}" unless assertion === type
17
17
  aexpr
18
18
  end
@@ -20,7 +20,9 @@ module Dhall
20
20
  def self.assert_types_match(a, b, message, context:)
21
21
  atype = self.for(a).annotate(context).type
22
22
  btype = self.for(b).annotate(context).type
23
- raise TypeError, "#{message}: #{atype}, #{btype}" unless atype == btype
23
+ unless atype.normalize == btype.normalize
24
+ raise TypeError, "#{message}: #{atype}, #{btype}"
25
+ end
24
26
  atype
25
27
  end
26
28
 
@@ -40,9 +42,13 @@ module Dhall
40
42
  @typecheckers[node_type] ||= [typechecker, extras]
41
43
  end
42
44
 
43
- def self.type_of(expr)
45
+ def self.annotate(expr)
44
46
  return if expr.nil?
45
- TypeChecker.for(expr).annotate(TypeChecker::Context.new).type
47
+ TypeChecker.for(expr).annotate(TypeChecker::Context.new)
48
+ end
49
+
50
+ def self.type_of(expr)
51
+ annotate(expr)&.type
46
52
  end
47
53
 
48
54
  class Context
@@ -233,6 +239,31 @@ module Dhall
233
239
  end
234
240
  end
235
241
 
242
+ class OperatorEquivalent
243
+ TypeChecker.register self, Dhall::Operator::Equivalent
244
+
245
+ def initialize(expr)
246
+ @expr = expr
247
+ @lhs = expr.lhs
248
+ @rhs = expr.rhs
249
+ end
250
+
251
+ def annotate(context)
252
+ type = TypeChecker.assert_types_match @lhs, @rhs,
253
+ "arguments do not match",
254
+ context: context
255
+
256
+ TypeChecker.assert_type type, Builtins[:Type],
257
+ "arguments are not terms",
258
+ context: context
259
+
260
+ Dhall::TypeAnnotation.new(
261
+ value: @expr.with(lhs: @lhs.annotate(type), rhs: @rhs.annotate(type)),
262
+ type: Builtins[:Type]
263
+ )
264
+ end
265
+ end
266
+
236
267
  class OperatorListConcatenate
237
268
  TypeChecker.register self, Dhall::Operator::ListConcatenate
238
269
 
@@ -309,10 +340,6 @@ module Dhall
309
340
  "RecursiveRecordMerge got",
310
341
  context: context
311
342
 
312
- TypeChecker.assert_types_match annotated_lhs.type, annotated_rhs.type,
313
- "RecursiveRecordMerge got mixed kinds",
314
- context: context
315
-
316
343
  [annotated_lhs, annotated_rhs]
317
344
  end
318
345
 
@@ -336,12 +363,6 @@ module Dhall
336
363
  end
337
364
 
338
365
  def annotate(context)
339
- kind = TypeChecker.assert_types_match(
340
- @expr.lhs, @expr.rhs,
341
- "RecursiveRecordTypeMerge mixed kinds",
342
- context: context
343
- )
344
-
345
366
  type = @expr.lhs.deep_merge_type(@expr.rhs)
346
367
 
347
368
  TypeChecker.assert type, Dhall::RecordType,
@@ -350,7 +371,13 @@ module Dhall
350
371
  # Annotate to sanity check
351
372
  TypeChecker.for(type).annotate(context)
352
373
 
353
- Dhall::TypeAnnotation.new(value: @expr, type: kind)
374
+ Dhall::TypeAnnotation.new(value: @expr, type: kind(context))
375
+ end
376
+
377
+ def kind(context)
378
+ lhs_kind = KINDS.index(TypeChecker.for(@expr.lhs).annotate(context).type)
379
+ rhs_kind = KINDS.index(TypeChecker.for(@expr.rhs).annotate(context).type)
380
+ KINDS[[lhs_kind, rhs_kind].max]
354
381
  end
355
382
  end
356
383
 
@@ -358,10 +385,16 @@ module Dhall
358
385
  TypeChecker.register self, Dhall::EmptyList
359
386
 
360
387
  def initialize(expr)
361
- @expr = expr
388
+ @expr = expr.with(type: expr.type.normalize)
362
389
  end
363
390
 
364
391
  def annotate(context)
392
+ TypeChecker.assert @expr.type, Dhall::Application,
393
+ "EmptyList unknown type #{@expr.type.inspect}"
394
+
395
+ TypeChecker.assert @expr.type.function, Builtins[:List],
396
+ "EmptyList unknown type #{@expr.type.inspect}"
397
+
365
398
  TypeChecker.assert_type @expr.element_type, Builtins[:Type],
366
399
  "EmptyList element type not of type Type",
367
400
  context: context
@@ -383,7 +416,7 @@ module Dhall
383
416
  end
384
417
 
385
418
  def annotation
386
- list = @alist.with(element_type: element_type)
419
+ list = @alist.with(type: Builtins[:List].call(element_type))
387
420
  Dhall::TypeAnnotation.new(type: list.type, value: list)
388
421
  end
389
422
 
@@ -468,13 +501,20 @@ module Dhall
468
501
 
469
502
  class AnonymousType
470
503
  TypeChecker.register self, Dhall::RecordType
471
- TypeChecker.register self, Dhall::UnionType
472
504
 
473
505
  def initialize(type)
474
506
  @type = type
475
507
  end
476
508
 
477
509
  def annotate(context)
510
+ kinds = check(context)
511
+ type = kinds.max_by { |k| KINDS.index(k) } || KINDS.first
512
+ Dhall::TypeAnnotation.new(value: @type, type: type)
513
+ end
514
+
515
+ protected
516
+
517
+ def check(context)
478
518
  kinds = @type.record.values.compact.map do |mtype|
479
519
  TypeChecker.for(mtype).annotate(context).type
480
520
  end
@@ -482,6 +522,16 @@ module Dhall
482
522
  TypeChecker.assert (kinds - KINDS), [],
483
523
  "AnonymousType field kind not one of #{KINDS}"
484
524
 
525
+ kinds
526
+ end
527
+ end
528
+
529
+ class UnionType < AnonymousType
530
+ TypeChecker.register self, Dhall::UnionType
531
+
532
+ def annotate(context)
533
+ kinds = check(context)
534
+
485
535
  TypeChecker.assert kinds, Util::ArrayAllTheSame,
486
536
  "AnonymousType field kinds not all the same"
487
537
 
@@ -537,12 +587,13 @@ module Dhall
537
587
 
538
588
  class Selector
539
589
  def self.for(annotated_record)
540
- if annotated_record.type == Builtins[:Type]
590
+ typ = annotated_record.type.normalize
591
+ if KINDS.include?(typ)
541
592
  TypeSelector.new(annotated_record.value)
542
- elsif annotated_record.type.class == Dhall::RecordType
543
- new(annotated_record.type)
593
+ elsif typ.class == Dhall::RecordType
594
+ new(typ)
544
595
  else
545
- raise TypeError, "RecordSelection on #{annotated_record.type}"
596
+ raise TypeError, "RecordSelection on #{typ}"
546
597
  end
547
598
  end
548
599
 
@@ -604,6 +655,33 @@ module Dhall
604
655
  end
605
656
  end
606
657
 
658
+ class RecordProjectionByExpression
659
+ TypeChecker.register self, Dhall::RecordProjectionByExpression
660
+
661
+ def initialize(projection)
662
+ @selector = projection.selector.normalize
663
+ @project_by_expression = projection
664
+ @project_by_keys = Dhall::RecordProjection.for(
665
+ @project_by_expression.record,
666
+ @selector.keys
667
+ )
668
+ end
669
+
670
+ def annotate(context)
671
+ TypeChecker.assert @selector, Dhall::RecordType,
672
+ "RecordProjectionByExpression on #{@selector.class}"
673
+
674
+ TypeChecker.assert_type @project_by_keys, @selector,
675
+ "Type doesn't match #{@selector}",
676
+ context: context
677
+
678
+ Dhall::TypeAnnotation.new(
679
+ value: @project_by_expression,
680
+ type: @selector
681
+ )
682
+ end
683
+ end
684
+
607
685
  class Enum
608
686
  TypeChecker.register self, Dhall::Enum
609
687
 
@@ -648,6 +726,39 @@ module Dhall
648
726
  end
649
727
  end
650
728
 
729
+ class ToMap
730
+ TypeChecker.register self, Dhall::ToMap
731
+
732
+ def initialize(tomap)
733
+ @tomap = tomap
734
+ @record = TypeChecker.for(tomap.record)
735
+ end
736
+
737
+ def check_annotation(record_type)
738
+ if record_type.is_a?(Dhall::EmptyRecordType)
739
+ TypeChecker.assert @tomap.type, Dhall::Expression,
740
+ "toMap {=} has no annotation"
741
+ else
742
+ t = Types::MAP(v: record_type.record.values.first)
743
+
744
+ TypeChecker.assert t, (@tomap.type || t),
745
+ "toMap does not match annotation"
746
+ end
747
+ end
748
+
749
+ def annotate(context)
750
+ record_type = @record.annotate(context).type
751
+ TypeChecker.assert record_type, Dhall::RecordType,
752
+ "toMap on a non-record: #{record_type.inspect}"
753
+
754
+ TypeChecker.assert record_type.record.values, Util::ArrayAllTheSame,
755
+ "toMap heterogenous: #{record_type.inspect}"
756
+
757
+ type = check_annotation(record_type)
758
+ Dhall::TypeAnnotation.new(value: @tomap, type: type)
759
+ end
760
+ end
761
+
651
762
  class Merge
652
763
  TypeChecker.register self, Dhall::Merge
653
764
 
@@ -678,7 +789,7 @@ module Dhall
678
789
  end
679
790
 
680
791
  def keys
681
- @type.record.keys
792
+ Set.new(@type.record.keys)
682
793
  end
683
794
 
684
795
  def fetch_input_type(k)
@@ -729,8 +840,8 @@ module Dhall
729
840
  end
730
841
 
731
842
  def assert_union_and_handlers_match
732
- extras = @handlers.keys - @union.type.alternatives.keys
733
- TypeChecker.assert extras, [],
843
+ extras = @handlers.keys ^ @union.type.alternatives.keys
844
+ TypeChecker.assert extras.to_a, [],
734
845
  "Merge handlers unknown alternatives: #{extras}"
735
846
 
736
847
  @union.type.alternatives.each do |k, atype|
@@ -771,8 +882,6 @@ module Dhall
771
882
  raise TypeError, "FunctionType part of this is a term"
772
883
  end
773
884
 
774
- raise TypeError, "Dependent types are not allowed" if outkind > inkind
775
-
776
885
  if outkind.zero?
777
886
  Term.new
778
887
  else
@@ -860,8 +969,6 @@ module Dhall
860
969
  end
861
970
  end
862
971
 
863
- TypeChecker.register ->(blk) { LetIn.for(blk.unflatten) }, Dhall::LetBlock
864
-
865
972
  class LetIn
866
973
  TypeChecker.register self, Dhall::LetIn
867
974
 
@@ -927,6 +1034,30 @@ module Dhall
927
1034
  end
928
1035
  end
929
1036
 
1037
+ class Assertion
1038
+ TypeChecker.register self, Dhall::Assertion
1039
+
1040
+ def initialize(expr)
1041
+ @expr = expr
1042
+ @type = expr.type
1043
+ end
1044
+
1045
+ def annotate(context)
1046
+ TypeChecker.assert @type, Dhall::Operator::Equivalent,
1047
+ "assert expected === got: #{@type.class}"
1048
+
1049
+ TypeChecker.assert_type @type, Builtins[:Type],
1050
+ "=== expected to have type Type",
1051
+ context: context
1052
+
1053
+ TypeChecker.assert @type.lhs.normalize.to_binary,
1054
+ @type.rhs.normalize.to_binary,
1055
+ "assert equivalence not equivalent"
1056
+
1057
+ @expr.with(type: @type.normalize)
1058
+ end
1059
+ end
1060
+
930
1061
  BUILTIN_TYPES = {
931
1062
  "Bool" => Builtins[:Type],
932
1063
  "Type" => Builtins[:Kind],
@@ -989,6 +1120,11 @@ module Dhall
989
1120
  )
990
1121
  )
991
1122
  ),
1123
+ "Natural/subtract" => Dhall::Forall.of_arguments(
1124
+ Builtins[:Natural],
1125
+ Builtins[:Natural],
1126
+ body: Builtins[:Natural]
1127
+ ),
992
1128
  "Natural/isZero" => Dhall::Forall.of_arguments(
993
1129
  Builtins[:Natural],
994
1130
  body: Builtins[:Bool]
@@ -1209,12 +1345,13 @@ module Dhall
1209
1345
  TypeChecker.register self, Dhall::Builtin
1210
1346
 
1211
1347
  def self.for(builtin)
1212
- unfilled = builtin.unfill
1213
- if unfilled != builtin
1214
- TypeChecker.for(unfilled)
1215
- else
1216
- new(builtin)
1348
+ if builtin.is_a?(Dhall::BuiltinFunction)
1349
+ if (unfilled = builtin.unfill) != builtin
1350
+ return TypeChecker.for(unfilled)
1351
+ end
1217
1352
  end
1353
+
1354
+ new(builtin)
1218
1355
  end
1219
1356
 
1220
1357
  def initialize(builtin)
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dhall/builtins"
4
+
5
+ module Dhall
6
+ module Types
7
+ def self.MAP_ENTRY(k: Builtins[:Text], v: Builtins[:Text])
8
+ RecordType.new(
9
+ record: {
10
+ "mapKey" => k, "mapValue" => v
11
+ }
12
+ )
13
+ end
14
+
15
+ def self.MAP(k: Builtins[:Text], v: Builtins[:Text])
16
+ Builtins[:List].call(MAP_ENTRY(k: k, v: v))
17
+ end
18
+ end
19
+ end
data/lib/dhall/util.rb CHANGED
@@ -1,9 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "promise"
3
4
  require "timeout"
4
5
 
5
6
  module Dhall
6
7
  module Util
8
+ class LazyPromise < Promise
9
+ def initialize(&block)
10
+ super
11
+ @block = block
12
+ end
13
+
14
+ def subscribe(*args)
15
+ super
16
+
17
+ begin
18
+ fulfill(@block.call)
19
+ rescue => e
20
+ reject(e)
21
+ end
22
+ end
23
+ end
24
+
7
25
  class AllOf
8
26
  def initialize(*validators)
9
27
  @validators = validators
@@ -181,5 +199,36 @@ module Dhall
181
199
  def self.longest_common_prefix(a, b)
182
200
  a.zip(b).take_while { |(x, y)| x == y }.map(&:first)
183
201
  end
202
+
203
+ def self.indent_size(str)
204
+ if str.end_with?("\n")
205
+ 0
206
+ else
207
+ str
208
+ .scan(/^[ \t]*(?=[^ \t\n]|\Z)/)
209
+ .map(&:chars)
210
+ .reduce(&method(:longest_common_prefix))&.length.to_i
211
+ end
212
+ end
213
+
214
+ def self.path_components_to_uri(*components)
215
+ URI("/#{components.map(&method(:uri_escape)).join("/")}")
216
+ end
217
+
218
+ def self.uri_escape(s)
219
+ ::URI.encode_www_form_component(s).gsub("+", "%20")
220
+ end
221
+
222
+ def self.net_http_req_with_timeout(uri, req, timeout:)
223
+ Net::HTTP.start(
224
+ uri.hostname,
225
+ uri.port,
226
+ use_ssl: uri.scheme == "https",
227
+ open_timeout: timeout,
228
+ ssl_timeout: timeout,
229
+ read_timeout: timeout,
230
+ write_timeout: timeout
231
+ ) { |http| http.request(req) }
232
+ end
184
233
  end
185
234
  end
data/lib/dhall.rb CHANGED
@@ -9,6 +9,7 @@ require "dhall/normalize"
9
9
  require "dhall/parser"
10
10
  require "dhall/resolve"
11
11
  require "dhall/typecheck"
12
+ require "dhall/types"
12
13
 
13
14
  module Dhall
14
15
  using Dhall::AsDhall
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dhall
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Paul Weber
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-13 00:00:00.000000000 Z
11
+ date: 2021-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: base32
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.3.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.3.2
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: cbor
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +52,34 @@ dependencies:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
54
  version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: lazy_object
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.0.3
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.0.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: multihashes
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.2.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.2.0
41
83
  - !ruby/object:Gem::Dependency
42
84
  name: promise.rb
43
85
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +122,20 @@ dependencies:
80
122
  - - "~>"
81
123
  - !ruby/object:Gem::Version
82
124
  version: 0.0.1
125
+ - !ruby/object:Gem::Dependency
126
+ name: minitest-fail-fast
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.1.0
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 0.1.0
83
139
  - !ruby/object:Gem::Dependency
84
140
  name: simplecov
85
141
  requirement: !ruby/object:Gem::Requirement
@@ -137,8 +193,8 @@ files:
137
193
  - lib/dhall/parser.rb
138
194
  - lib/dhall/resolve.rb
139
195
  - lib/dhall/typecheck.rb
196
+ - lib/dhall/types.rb
140
197
  - lib/dhall/util.rb
141
- - lib/dhall/visitor.rb
142
198
  homepage: https://git.sr.ht/~singpolyma/dhall-ruby
143
199
  licenses:
144
200
  - GPL-3.0
@@ -159,7 +215,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
159
215
  version: '0'
160
216
  requirements: []
161
217
  rubyforge_project:
162
- rubygems_version: 2.5.2.1
218
+ rubygems_version: 2.7.6.2
163
219
  signing_key:
164
220
  specification_version: 4
165
221
  summary: The non-repetitive alternative to YAML, in Ruby