algebrick 0.4.0 → 0.5.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -3
  3. data/README_FULL.md +13 -21
  4. data/VERSION +1 -1
  5. data/doc/actor.rb +21 -0
  6. data/doc/data.in.rb +99 -0
  7. data/doc/data.out.rb +103 -0
  8. data/doc/extending_behavior.in.rb +61 -0
  9. data/doc/extending_behavior.out.rb +62 -0
  10. data/doc/format.rb +75 -0
  11. data/doc/init.rb +1 -0
  12. data/doc/json.in.rb +39 -0
  13. data/doc/json.out.rb +43 -0
  14. data/doc/null.in.rb +36 -0
  15. data/doc/null.out.rb +40 -0
  16. data/doc/parametrized.in.rb +37 -0
  17. data/doc/parametrized.out.rb +41 -0
  18. data/doc/pattern_matching.in.rb +116 -0
  19. data/doc/pattern_matching.out.rb +122 -0
  20. data/doc/quick_example.in.rb +27 -0
  21. data/doc/quick_example.out.rb +27 -0
  22. data/doc/tree1.in.rb +10 -0
  23. data/doc/tree1.out.rb +10 -0
  24. data/doc/type_def.in.rb +21 -0
  25. data/doc/type_def.out.rb +21 -0
  26. data/doc/values.in.rb +52 -0
  27. data/doc/values.out.rb +58 -0
  28. data/lib/algebrick/atom.rb +49 -0
  29. data/lib/algebrick/dsl.rb +104 -0
  30. data/lib/algebrick/field_method_readers.rb +43 -0
  31. data/lib/algebrick/matcher_delegations.rb +45 -0
  32. data/lib/algebrick/matchers/abstract.rb +127 -0
  33. data/lib/algebrick/matchers/abstract_logic.rb +38 -0
  34. data/lib/algebrick/matchers/and.rb +29 -0
  35. data/lib/algebrick/matchers/any.rb +37 -0
  36. data/lib/algebrick/matchers/array.rb +57 -0
  37. data/lib/algebrick/matchers/atom.rb +28 -0
  38. data/lib/algebrick/matchers/not.rb +44 -0
  39. data/lib/algebrick/matchers/or.rb +51 -0
  40. data/lib/algebrick/matchers/product.rb +73 -0
  41. data/lib/algebrick/matchers/variant.rb +29 -0
  42. data/lib/algebrick/matchers/wrapper.rb +57 -0
  43. data/lib/algebrick/matchers.rb +31 -0
  44. data/lib/algebrick/matching.rb +62 -0
  45. data/lib/algebrick/parametrized_type.rb +122 -0
  46. data/lib/algebrick/product_constructors/abstract.rb +70 -0
  47. data/lib/algebrick/product_constructors/basic.rb +47 -0
  48. data/lib/algebrick/product_constructors/named.rb +58 -0
  49. data/lib/algebrick/product_constructors.rb +25 -0
  50. data/lib/algebrick/product_variant.rb +195 -0
  51. data/lib/algebrick/reclude.rb +39 -0
  52. data/lib/algebrick/serializer.rb +129 -0
  53. data/lib/algebrick/serializers.rb +25 -0
  54. data/lib/algebrick/type.rb +61 -0
  55. data/lib/algebrick/type_check.rb +58 -0
  56. data/lib/algebrick/types.rb +59 -0
  57. data/lib/algebrick/value.rb +41 -0
  58. data/lib/algebrick.rb +14 -1170
  59. data/spec/algebrick_test.rb +708 -0
  60. metadata +105 -27
data/lib/algebrick.rb CHANGED
@@ -21,8 +21,6 @@
21
21
  # TODO gemmify reclude
22
22
  # TODO gemmify typecheck
23
23
 
24
- require 'monitor'
25
-
26
24
 
27
25
  # Provides Algebraic types and pattern matching
28
26
  #
@@ -34,1173 +32,19 @@ module Algebrick
34
32
  @version ||= Gem::Version.new File.read(File.join(File.dirname(__FILE__), '..', 'VERSION'))
35
33
  end
36
34
 
37
- # fix module to re-include itself to where it was already included when a module is included into it
38
- module Reclude
39
- def included(base)
40
- included_into << base
41
- super base
42
- end
43
-
44
- def include(*modules)
45
- super(*modules)
46
- modules.reverse.each do |module_being_included|
47
- included_into.each do |mod|
48
- mod.send :include, module_being_included
49
- end
50
- end
51
- end
52
-
53
- private
54
-
55
- def included_into
56
- @included_into ||= []
57
- end
58
- end
59
-
60
- module TypeCheck
61
- # FIND: type checking of collections?
62
-
63
- def Type?(value, *types)
64
- types.any? { |t| value.is_a? t }
65
- end
66
-
67
- def Type!(value, *types)
68
- Type?(value, *types) or
69
- TypeCheck.error(value, 'is not', types)
70
- value
71
- end
72
-
73
- def Match?(value, *types)
74
- types.any? { |t| t === value }
75
- end
76
-
77
- def Match!(value, *types)
78
- Match?(value, *types) or
79
- TypeCheck.error(value, 'is not matching', types)
80
- value
81
- end
82
-
83
- def Child?(value, *types)
84
- Type?(value, Class) &&
85
- types.any? { |t| value <= t }
86
- end
87
-
88
- def Child!(value, *types)
89
- Child?(value, *types) or
90
- TypeCheck.error(value, 'is not child', types)
91
- value
92
- end
93
-
94
- private
95
-
96
- def self.error(value, message, types)
97
- raise TypeError,
98
- "Value (#{value.class}) '#{value}' #{message} any of: #{types.join('; ')}."
99
- end
100
- end
101
-
102
- # include this module anywhere yoy need to use pattern matching
103
- module Matching
104
- def any
105
- Matchers::Any.new
106
- end
107
-
108
- def match(value, *cases)
109
- cases = if cases.size == 1 && cases.first.is_a?(Hash)
110
- cases.first
111
- else
112
- cases
113
- end
114
-
115
- cases.each do |matcher, block|
116
- return Matching.match_value matcher, block if matcher === value
117
- end
118
- raise "no match for (#{value.class}) '#{value}' by any of #{cases.map(&:first).join ', '}"
119
- end
120
-
121
- def on(matcher, value = nil, &block)
122
- matcher = if matcher.is_a? Matchers::Abstract
123
- matcher
124
- else
125
- matcher.to_m
126
- end
127
- raise ArgumentError, 'only one of block or value can be supplied' if block && value
128
- [matcher, value || block]
129
- end
130
-
131
- # FIND: #match! raise when match is not complete on a given type
132
-
133
- private
134
-
135
- def self.match_value(matcher, block)
136
- if block.kind_of? Proc
137
- if matcher.kind_of? Matchers::Abstract
138
- matcher.assigns &block
139
- else
140
- block.call
141
- end
142
- else
143
- block
144
- end
145
- end
146
- end
147
-
148
- include Matching
149
- extend Matching
150
-
151
- module MatcherDelegations
152
- def ~
153
- ~to_m
154
- end
155
-
156
- def &(other)
157
- to_m & other
158
- end
159
-
160
- def |(other)
161
- to_m | other
162
- end
163
-
164
- def !
165
- !to_m
166
- end
167
-
168
- def case(&block)
169
- to_m.case &block
170
- end
171
-
172
- def >>(block)
173
- to_m >> block
174
- end
175
-
176
- def >(block)
177
- to_m > block
178
- end
179
- end
180
-
181
- # Any Algebraic type defined by Algebrick is kind of Type
182
- class Type < Module
183
- include TypeCheck
184
- include Matching
185
- include MatcherDelegations
186
- include Reclude
187
-
188
- def initialize(name, &definition)
189
- super &definition
190
- @name = name
191
- end
192
-
193
- def name
194
- super || @name
195
- end
196
-
197
- def to_m(*args)
198
- raise NotImplementedError
199
- end
200
-
201
- def ==(other)
202
- raise NotImplementedError
203
- end
204
-
205
- def be_kind_of(type)
206
- raise NotImplementedError
207
- end
208
-
209
- def to_s
210
- raise NotImplementedError
211
- end
212
-
213
- def inspect
214
- to_s
215
- end
216
- end
217
-
218
- # Any value of Algebraic type is kind of Value
219
- module Value
220
- include TypeCheck
221
- include Matching
222
-
223
- def ==(other)
224
- raise NotImplementedError
225
- end
226
-
227
- def type
228
- raise NotImplementedError
229
- end
230
-
231
- def to_hash
232
- raise NotImplementedError
233
- end
234
-
235
- def to_s
236
- raise NotImplementedError
237
- end
238
-
239
- def inspect
240
- to_s
241
- end
242
- end
243
-
244
- TYPE_KEY = :algebrick
245
- FIELDS_KEY = :fields
246
-
247
- # Representation of Atomic types
248
- class Atom < Type
249
- include Value
250
-
251
- def initialize(name, &block)
252
- super name, &block
253
- extend self
254
- end
255
-
256
- def to_m
257
- Matchers::Atom.new self
258
- end
259
-
260
- def be_kind_of(type)
261
- extend type
262
- end
263
-
264
- def ==(other)
265
- self.equal? other
266
- end
267
-
268
- def type
269
- self
270
- end
271
-
272
- def to_s
273
- name
274
- end
275
-
276
- def to_hash
277
- { TYPE_KEY => name }
278
- end
279
-
280
- def from_hash(hash)
281
- if hash == to_hash
282
- self
283
- else
284
- raise ArgumentError
285
- end
286
- end
287
- end
288
-
289
- # A private class used for Product values creation
290
- class ProductConstructor
291
- include Value
292
- attr_reader :fields
293
-
294
- def initialize(*fields)
295
- if fields.size == 1 && fields.first.is_a?(Hash)
296
- fields = type.field_names.map { |k| fields.first[k] }
297
- end
298
- @fields = fields.zip(self.class.type.fields).map { |field, type| Type! field, type }.freeze
299
- end
300
-
301
- def to_s
302
- "#{self.class.type.name}[" +
303
- if type.field_names?
304
- type.field_names.map { |name| "#{name}: #{self[name].to_s}" }.join(', ')
305
- else
306
- fields.map(&:to_s).join(', ')
307
- end + ']'
308
- end
309
-
310
- def pretty_print(q)
311
- q.group(1, "#{self.class.type.name}[", ']') do
312
- if type.field_names?
313
- type.field_names.each_with_index do |name, i|
314
- if i == 0
315
- q.breakable ''
316
- else
317
- q.text ','
318
- q.breakable ' '
319
- end
320
- q.text name.to_s
321
- q.text ':'
322
- q.group(1) do
323
- q.breakable ' '
324
- q.pp self[name]
325
- end
326
- end
327
- else
328
- fields.each_with_index do |value, i|
329
- if i == 0
330
- q.breakable ''
331
- else
332
- q.text ','
333
- q.breakable ' '
334
- end
335
- q.pp value
336
- end
337
- end
338
- end
339
- end
340
-
341
- def to_ary
342
- @fields
343
- end
344
-
345
- def to_a
346
- @fields
347
- end
348
-
349
- def to_hash
350
- { TYPE_KEY => self.class.type.name }.
351
- update(if type.field_names?
352
- type.field_names.inject({}) { |h, name| h.update name => hashize(self[name]) }
353
- else
354
- { FIELDS_KEY => fields.map { |v| hashize v } }
355
- end)
356
- end
357
-
358
- def ==(other)
359
- return false unless other.kind_of? self.class
360
- @fields == other.fields
361
- end
362
-
363
- def self.type
364
- @type || raise
365
- end
366
-
367
- def type
368
- self.class.type
369
- end
370
-
371
- def self.name
372
- @type.to_s
373
- end
374
-
375
- def self.to_s
376
- name
377
- end
378
-
379
- def self.type=(type)
380
- raise if @type
381
- @type = type
382
- include type
383
- end
384
-
385
- private
386
-
387
- def hashize(value)
388
- (value.respond_to? :to_hash) ? value.to_hash : value
389
- end
390
- end
391
-
392
- # Representation of Product and Variant types. The class behaves differently
393
- # based on #kind.
394
- class ProductVariant < Type
395
- attr_reader :fields, :variants
396
-
397
- def initialize(name, &definition)
398
- super(name, &definition)
399
- @to_be_kind_of = []
400
- end
401
-
402
- def set_fields(fields_or_hash)
403
- raise TypeError, 'can be set only once' if @fields
404
- fields, keys = case fields_or_hash
405
- when Hash
406
- [fields_or_hash.values, fields_or_hash.keys]
407
- when Array
408
- [fields_or_hash, nil]
409
- else
410
- raise ArgumentError
411
- end
412
-
413
- add_field_names keys if keys
414
-
415
- fields.all? { |f| Type! f, Type, Class, Module }
416
- raise TypeError, 'there is no product with zero fields' unless fields.size > 0
417
- define_method(:value) { @fields.first } if fields.size == 1
418
- @fields = fields
419
- @constructor = Class.new(ProductConstructor).tap { |c| c.type = self }
420
- apply_be_kind_of
421
- self
422
- end
423
-
424
- def field_names
425
- @field_names or raise TypeError, "field names not defined on #{self}"
426
- end
427
-
428
- def field_names?
429
- !!@field_names
430
- end
431
-
432
- def field_indexes
433
- @field_indexes or raise TypeError, "field names not defined on #{self}"
434
- end
435
-
436
- def add_field_method_reader(field)
437
- raise TypeError, 'no field names' unless field_names?
438
- raise ArgumentError, "no field name #{field}" unless field_names.include? field
439
- raise ArgumentError, "method #{field} already defined" if instance_methods.include? field
440
- define_method(field) { self[field] }
441
- self
442
- end
443
-
444
- def add_field_method_readers(*fields)
445
- fields.each { |f| add_field_method_reader f }
446
- self
447
- end
448
-
449
- def add_all_field_method_readers
450
- add_field_method_readers *@field_names
451
- end
452
-
453
- def set_variants(variants)
454
- raise TypeError, 'can be set only once' if @variants
455
- variants.all? { |v| Type! v, Type, Class }
456
- @variants = variants
457
- apply_be_kind_of
458
- variants.each do |v|
459
- if v.respond_to? :be_kind_of
460
- v.be_kind_of self
461
- else
462
- v.send :include, self
463
- end
464
- end
465
- self
466
- end
467
-
468
- def new(*fields)
469
- raise TypeError, "#{self} does not have fields" unless @constructor
470
- @constructor.new *fields
471
- end
472
-
473
- alias_method :[], :new
474
-
475
- def ==(other)
476
- other.kind_of? ProductVariant and
477
- variants == other.variants and fields == other.fields
478
- end
479
-
480
- def be_kind_of(type)
481
- @to_be_kind_of << type
482
- apply_be_kind_of
483
- end
484
-
485
- def apply_be_kind_of
486
- @to_be_kind_of.each do |type|
487
- @constructor.send :include, type if @constructor
488
- variants.each { |v| v.be_kind_of type unless v == self } if @variants
489
- end
490
- end
491
-
492
- def call(*field_matchers)
493
- raise TypeError, "#{self} does not have any fields" unless @fields
494
- Matchers::Product.new self, *field_matchers
495
- end
496
-
497
- def to_m
498
- case kind
499
- when :product
500
- Matchers::Product.new self
501
- when :product_variant
502
- Matchers::Variant.new self
503
- when :variant
504
- Matchers::Variant.new self
505
- else
506
- raise
507
- end
508
- end
509
-
510
- def to_s
511
- case kind
512
- when :product
513
- product_to_s
514
- when :product_variant
515
- name + '(' +
516
- variants.map do |variant|
517
- if variant == self
518
- product_to_s
519
- else
520
- variant.name
521
- end
522
- end.join(' | ') +
523
- ')'
524
- when :variant
525
- "#{name}(#{variants.map(&:name).join ' | '})"
526
- else
527
- raise
528
- end
529
- end
530
-
531
- def from_hash(hash)
532
- case kind
533
- when :product
534
- product_from_hash hash
535
- when :product_variant
536
- product_from_hash hash
537
- when :variant
538
- field_from_hash hash
539
- else
540
- raise
541
- end
542
- end
543
-
544
- def kind
545
- case
546
- when @fields && !@variants
547
- :product
548
- when @fields && @variants
549
- :product_variant
550
- when !@fields && @variants
551
- :variant
552
- when !@fields && !@variants
553
- raise TypeError, 'fields or variants have to be set'
554
- end
555
- end
556
-
557
- def assigned_types
558
- @assigned_types or raise TypeError, "#{self} does not have assigned types"
559
- end
560
-
561
- def assigned_types=(assigned_types)
562
- raise TypeError, "#{self} assigned types already set" if @assigned_types
563
- @assigned_types = assigned_types
564
- end
565
-
566
- private
567
-
568
- def product_to_s
569
- fields_str = if field_names?
570
- field_names.zip(fields).map { |name, field| "#{name}: #{field.name}" }
571
- else
572
- fields.map(&:name)
573
- end
574
- "#{name}(#{fields_str.join ', '})"
575
- end
576
-
577
- def add_field_names(names)
578
- @field_names = names
579
- names.all? { |k| Type! k, Symbol }
580
- dict = @field_indexes =
581
- Hash.new { |h, k| raise ArgumentError, "unknown field #{k.inspect} in #{self}" }.
582
- update names.each_with_index.inject({}) { |h, (k, i)| h.update k => i }
583
- define_method(:[]) { |key| @fields[dict[key]] }
584
- end
585
-
586
- def product_from_hash(hash)
587
- (type_name = hash[TYPE_KEY] || hash[TYPE_KEY.to_s]) or
588
- raise ArgumentError, "hash does not have #{TYPE_KEY}"
589
- raise ArgumentError, "#{type_name} is not #{name}" unless type_name == name
590
-
591
- fields = hash[FIELDS_KEY] || hash[FIELDS_KEY.to_s] ||
592
- hash.reject { |k, _| k.to_s == TYPE_KEY.to_s }
593
- Type! fields, Hash, Array
594
-
595
- case fields
596
- when Array
597
- self[*fields.map { |value| field_from_hash value }]
598
- when Hash
599
- self[fields.inject({}) do |h, (name, value)|
600
- raise ArgumentError unless field_names.map(&:to_s).include? name.to_s
601
- h.update name.to_sym => field_from_hash(value)
602
- end]
603
- end
604
- end
605
-
606
- def field_from_hash(hash)
607
- return hash unless Hash === hash
608
- (type_name = hash[TYPE_KEY] || hash[TYPE_KEY.to_s]) or return hash
609
- type = constantize type_name
610
- type.from_hash hash
611
- end
612
-
613
- def constantize(camel_cased_word)
614
- names = camel_cased_word.split('::')
615
- names.shift if names.empty? || names.first.empty?
616
-
617
- constant = Object
618
- names.each do |name|
619
- constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
620
- end
621
- constant
622
- end
623
- end
624
-
625
- class ParametrizedType < Module
626
- include TypeCheck
627
- include MatcherDelegations
628
-
629
- attr_reader :variables, :fields, :variants
630
-
631
- def initialize(variables)
632
- @variables = variables.each { |v| Type! v, Symbol }
633
- @fields = nil
634
- @variants = nil
635
- @cache = {}
636
- @cache_barrier = Monitor.new
637
- end
638
-
639
- def set_fields(fields)
640
- @fields = Type! fields, Hash, Array
641
- end
642
-
643
- def field_names
644
- case @fields
645
- when Hash
646
- @fields.keys
647
- when Array, nil
648
- raise TypeError, "field names not defined on #{self}"
649
- else
650
- raise
651
- end
652
- end
653
-
654
- def set_variants(variants)
655
- @variants = Type! variants, Array
656
- end
657
-
658
- def [](*assigned_types)
659
- @cache_barrier.synchronize do
660
- @cache[assigned_types] || begin
661
- raise ArgumentError unless assigned_types.size == variables.size
662
- ProductVariant.new(type_name(assigned_types)).tap do |type|
663
- type.be_kind_of self
664
- @cache[assigned_types] = type
665
- type.assigned_types = assigned_types
666
- type.set_variants insert_types(variants, assigned_types) if variants
667
- type.set_fields insert_types(fields, assigned_types) if fields
668
- end
669
- end
670
- end
671
- end
672
-
673
- def to_s
674
- "#{name}[#{variables.join(', ')}]"
675
- end
676
-
677
- def inspect
678
- to_s
679
- end
680
-
681
- def to_m
682
- if @variants
683
- Matchers::Variant.new self
684
- else
685
- Matchers::Product.new self
686
- end
687
- end
688
-
689
- def call(*field_matchers)
690
- raise TypeError unless @fields
691
- Matchers::Product.new self, *field_matchers
692
- end
693
-
694
- private
695
-
696
- def insert_types(types, assigned_types)
697
- case types
698
- when Hash
699
- types.inject({}) { |h, (k, v)| h.update k => insert_type(v, assigned_types) }
700
- when Array
701
- types.map { |v| insert_type v, assigned_types }
702
- else
703
- raise ArgumentError
704
- end
705
- end
706
-
707
- def insert_type(type, assigned_types)
708
- case type
709
- when Symbol
710
- assigned_types[variables.index type]
711
- when ParametrizedType
712
- type[*type.variables.map { |v| assigned_types[variables.index v] }]
713
- else
714
- type
715
- end
716
- end
717
-
718
- def type_name(assigned_types)
719
- "#{name}[#{assigned_types.join(', ')}]"
720
- end
721
- end
722
-
723
- module DSL
724
- module Shortcuts
725
- def type(*variables, &block)
726
- Algebrick.type *variables, &block
727
- end
728
-
729
- def atom
730
- Algebrick.atom
731
- end
732
- end
733
-
734
- class TypeDefinitionScope
735
- include Shortcuts
736
- include TypeCheck
737
-
738
- attr_reader :new_type
739
-
740
- def initialize(new_type, &block)
741
- @new_type = Type! new_type, ProductVariant, ParametrizedType
742
- instance_exec @new_type, &block
743
- @new_type.kind if @new_type.is_a? ProductVariant
744
- end
745
-
746
- def fields(*fields)
747
- @new_type.set_fields fields.first.is_a?(Hash) ? fields.first : fields
748
- self
749
- end
750
-
751
- def fields!(*fields)
752
- fields(*fields)
753
- all_readers
754
- end
755
-
756
- def variants(*variants)
757
- @new_type.set_variants variants
758
- self
759
- end
760
-
761
- def field_readers(*names)
762
- @new_type.add_field_method_readers *names
763
- self
764
- end
765
-
766
- alias_method :readers, :field_readers
767
-
768
- def all_field_readers
769
- @new_type.add_all_field_method_readers
770
- self
771
- end
772
-
773
- alias_method :all_readers, :all_field_readers
774
- end
775
-
776
- class OuterShell
777
- include Shortcuts
778
-
779
- def initialize(&block)
780
- instance_eval &block
781
- end
782
- end
783
- end
784
-
785
- def self.type(*variables, &block)
786
- if block.nil?
787
- raise 'Atom canot be parametrized' unless variables.empty?
788
- atom
789
- else
790
- if variables.empty?
791
- DSL::TypeDefinitionScope.new(ProductVariant.new(nil), &block).new_type
792
- else
793
- DSL::TypeDefinitionScope.new(ParametrizedType.new(variables), &block).new_type
794
- end
795
- end
796
- end
797
-
798
- def self.atom
799
- Atom.new nil
800
- end
801
-
802
- def self.types(&block)
803
- DSL::OuterShell.new &block
804
- end
805
-
806
- module Matchers
807
-
808
- class Abstract
809
- include TypeCheck
810
- attr_reader :value
811
-
812
- def initialize
813
- @assign, @value, @matched = nil
814
- end
815
-
816
- def case(&block)
817
- return self, block
818
- end
819
-
820
- alias_method :when, :case
821
-
822
- def >(block)
823
- return self, block
824
- end
825
-
826
- alias_method :>>, :>
827
-
828
- def ~
829
- @assign = true
830
- self
831
- end
832
-
833
- def &(matcher)
834
- And.new self, matcher
835
- end
836
-
837
- def |(matcher)
838
- Or.new self, matcher
839
- end
840
-
841
- def !
842
- Not.new self
843
- end
844
-
845
- def assign?
846
- @assign
847
- end
848
-
849
- def matched?
850
- @matched
851
- end
852
-
853
- def children_including_self
854
- children.unshift self
855
- end
856
-
857
- def assigns
858
- collect_assigns.tap do
859
- return yield *assigns if block_given?
860
- end
861
- end
862
-
863
- def to_a
864
- assigns
865
- end
866
-
867
- def ===(other)
868
- matching?(other).tap { |matched| @value = other if (@matched = matched) }
869
- end
870
-
871
- def assign_to_s
872
- assign? ? '~' : ''
873
- end
874
-
875
- def inspect
876
- to_s
877
- end
878
-
879
- def children
880
- raise NotImplementedError
881
- end
882
-
883
- def to_s
884
- raise NotImplementedError
885
- end
886
-
887
- def ==(other)
888
- raise NotImplementedError
889
- end
890
-
891
- protected
892
-
893
- def matching?(other)
894
- raise NotImplementedError
895
- end
896
-
897
- private
898
-
899
- def collect_assigns
900
- mine = @assign ? [@value] : []
901
- children.inject(mine) { |assigns, child| assigns + child.assigns }
902
- end
903
-
904
- def matchable!(obj)
905
- raise ArgumentError, 'object does not respond to :===' unless obj.respond_to? :===
906
- obj
907
- end
908
-
909
- def find_children(collection)
910
- collection.map do |matcher|
911
- matcher if matcher.kind_of? Abstract
912
- end.compact
913
- end
914
- end
915
-
916
- class AbstractLogic < Abstract
917
- def self.call(*matchers)
918
- new *matchers
919
- end
920
-
921
- attr_reader :matchers
922
-
923
- def initialize(*matchers)
924
- @matchers = matchers.each { |m| matchable! m }
925
- end
926
-
927
- def children
928
- find_children matchers
929
- end
930
-
931
- def ==(other)
932
- other.kind_of? self.class and
933
- self.matchers == other.matchers
934
- end
935
- end
936
-
937
- class And < AbstractLogic
938
- def to_s
939
- matchers.join ' & '
940
- end
941
-
942
- protected
943
-
944
- def matching?(other)
945
- matchers.all? { |m| m === other }
946
- end
947
- end
948
-
949
- class Or < AbstractLogic
950
- def to_s
951
- matchers.join ' | '
952
- end
953
-
954
- protected
955
-
956
- def matching?(other)
957
- matchers.any? { |m| m === other }
958
- end
959
-
960
- alias_method :super_children, :children
961
- private :super_children
962
-
963
- def children
964
- super.select &:matched?
965
- end
966
-
967
- private
968
-
969
- def collect_assigns
970
- super.tap do |assigns|
971
- missing = assigns_size - assigns.size
972
- assigns.push(*::Array.new(missing))
973
- end
974
- end
975
-
976
- def assigns_size
977
- # TODO is it efficient?
978
- super_children.map { |ch| ch.assigns.size }.max
979
- end
980
- end
981
-
982
- class Not < Abstract
983
- attr_reader :matcher
984
-
985
- def initialize(matcher)
986
- @matcher = matcher
987
- end
988
-
989
- def children
990
- []
991
- end
992
-
993
- def to_s
994
- '!' + matcher.to_s
995
- end
996
-
997
- def ==(other)
998
- other.kind_of? self.class and
999
- self.matcher == other.matcher
1000
- end
1001
-
1002
- protected
1003
-
1004
- def matching?(other)
1005
- not matcher === other
1006
- end
1007
- end
1008
-
1009
- class Any < Abstract
1010
- def children
1011
- []
1012
- end
1013
-
1014
- def to_s
1015
- assign_to_s + 'any'
1016
- end
1017
-
1018
- def ==(other)
1019
- other.kind_of? self.class
1020
- end
1021
-
1022
- protected
1023
-
1024
- def matching?(other)
1025
- true
1026
- end
1027
- end
1028
-
1029
- class Wrapper < Abstract
1030
- def self.call(something)
1031
- new something
1032
- end
1033
-
1034
- attr_reader :something
1035
-
1036
- def initialize(something)
1037
- super()
1038
- @something = matchable! something
1039
- end
1040
-
1041
- def children
1042
- find_children [@something]
1043
- end
1044
-
1045
- def to_s
1046
- assign_to_s + "Wrapper.(#{@something})"
1047
- end
1048
-
1049
- def ==(other)
1050
- other.kind_of? self.class and
1051
- self.something == other.something
1052
- end
1053
-
1054
- protected
1055
-
1056
- def matching?(other)
1057
- @something === other
1058
- end
1059
- end
1060
-
1061
- class ::Object
1062
- def to_m
1063
- Wrapper.new(self)
1064
- end
1065
- end
1066
-
1067
- class Array < Abstract
1068
- def self.call(*matchers)
1069
- new *matchers
1070
- end
1071
-
1072
- attr_reader :matchers
1073
-
1074
- def initialize(*matchers)
1075
- super()
1076
- @matchers = matchers
1077
- end
1078
-
1079
- def children
1080
- find_children @matchers
1081
- end
1082
-
1083
- def to_s
1084
- "#{assign_to_s}#{"Array.(#{matchers.join(',')})" if matchers}"
1085
- end
1086
-
1087
- def ==(other)
1088
- other.kind_of? self.class and
1089
- self.matchers == other.matchers
1090
- end
1091
-
1092
- protected
1093
-
1094
- def matching?(other)
1095
- other.kind_of? ::Array and
1096
- matchers.size == other.size and
1097
- matchers.each_with_index.all? { |m, i| m === other[i] }
1098
- end
1099
- end
1100
-
1101
- class ::Array
1102
- def self.call(*matchers)
1103
- Matchers::Array.new *matchers
1104
- end
1105
- end
1106
-
1107
- class Product < Abstract
1108
- attr_reader :algebraic_type, :field_matchers
1109
-
1110
- def initialize(algebraic_type, *field_matchers)
1111
- super()
1112
- @algebraic_type = Type! algebraic_type, Algebrick::ProductVariant, Algebrick::ParametrizedType
1113
- raise ArgumentError unless algebraic_type.fields
1114
- @field_matchers = case
1115
-
1116
- # AProduct.()
1117
- when field_matchers.empty?
1118
- ::Array.new(algebraic_type.fields.size) { Algebrick.any }
1119
-
1120
- # AProduct.(field_name: a_matcher)
1121
- when field_matchers.size == 1 && field_matchers.first.is_a?(Hash)
1122
- field_matchers = field_matchers.first
1123
- unless (dif = field_matchers.keys - algebraic_type.field_names).empty?
1124
- raise ArgumentError, "no #{dif} fields in #{algebraic_type}"
1125
- end
1126
- algebraic_type.field_names.map do |field|
1127
- field_matchers.key?(field) ? field_matchers[field] : Algebrick.any
1128
- end
1129
-
1130
- # normal
1131
- else
1132
- field_matchers
1133
- end
1134
- unless algebraic_type.fields.size == @field_matchers.size
1135
- raise ArgumentError
1136
- end
1137
- end
1138
-
1139
- def children
1140
- find_children @field_matchers
1141
- end
1142
-
1143
- def to_s
1144
- assign_to_s + "#{@algebraic_type.name}.(#{@field_matchers.join(', ')})"
1145
- end
1146
-
1147
- # TODO prety_print for all matchers
1148
-
1149
- def ==(other)
1150
- other.kind_of? self.class and
1151
- self.algebraic_type == other.algebraic_type and
1152
- self.field_matchers == other.field_matchers
1153
- end
1154
-
1155
- protected
1156
-
1157
- def matching?(other)
1158
- other.kind_of?(@algebraic_type) and other.kind_of?(ProductConstructor) and
1159
- @field_matchers.zip(other.fields).all? do |matcher, field|
1160
- matcher === field
1161
- end
1162
- end
1163
- end
1164
-
1165
- class Variant < Wrapper
1166
- def initialize(something)
1167
- raise ArgumentError unless something.variants
1168
- Type! something, Algebrick::ProductVariant
1169
- super something
1170
- end
1171
-
1172
- def to_s
1173
- assign_to_s + "#{@something.name}.to_m"
1174
- end
1175
- end
1176
-
1177
- class Atom < Wrapper
1178
- def initialize(something)
1179
- Type! something, Algebrick::Atom
1180
- super something
1181
- end
1182
-
1183
- def to_s
1184
- assign_to_s + "#{@something.name}.to_m"
1185
- end
1186
- end
1187
- end
1188
-
1189
- module Types
1190
- Maybe = Algebrick.type(:v) do
1191
- variants None = atom,
1192
- Some = type(:v) { fields :v }
1193
- end
1194
-
1195
- module Maybe
1196
- def maybe
1197
- match self,
1198
- None >> nil,
1199
- Some >-> { yield value }
1200
- end
1201
- end
1202
- end
1203
-
1204
- include Types
35
+ require 'algebrick/reclude'
36
+ require 'algebrick/type_check'
37
+ require 'algebrick/matching'
38
+ require 'algebrick/matcher_delegations'
39
+ require 'algebrick/type'
40
+ require 'algebrick/value'
41
+ require 'algebrick/atom'
42
+ require 'algebrick/product_constructors'
43
+ require 'algebrick/field_method_readers'
44
+ require 'algebrick/product_variant'
45
+ require 'algebrick/parametrized_type'
46
+ require 'algebrick/dsl'
47
+ require 'algebrick/matchers'
48
+ require 'algebrick/types'
1205
49
 
1206
50
  end