algebrick 0.1.3 → 0.2.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 (6) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -7
  3. data/README_FULL.md +10 -11
  4. data/VERSION +1 -1
  5. data/lib/algebrick.rb +770 -348
  6. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fb30a6eeabbe7769c1f946395bef047d5acf4362
4
- data.tar.gz: 5c0289625f919dffe3f57e6fa2e258ca353d4ba9
3
+ metadata.gz: 7f70cafbfe1296efd6042757bf59917fb2f290aa
4
+ data.tar.gz: 3466daa431dd466059085c9e6acf42eb872a7335
5
5
  SHA512:
6
- metadata.gz: 0932e96464a044f3e2a9138efcd07433bf17fb747f1718b73354599750b84543e70f7b29a97db0854343f6f25c04eda02bc6baa8498a90e5feb55f2ac57437ad
7
- data.tar.gz: 7bf6898feafadd9694cf898b61c81c9879fcc0dc628044244eed4f9ec5c8d1b3d2888b826305a73d463e08621f79e6759a75b5c84283a4bb48c617d27469671f
6
+ metadata.gz: 37ea095790d58c8995686a70e4a25936db720d40f28b35ebdefab1e25da4395124cca6aed2f93fd5ee7f0d6d82532d55e05d428121e824b8ffcdb9829ac3b0a7
7
+ data.tar.gz: 779756c205b84c25f756ad8f2d8658806d9023db3c015f848ffcfb69d6b157686287fa5d3aa06c95745924d70192c87e10d78ed5ddd2cb22e5047d332d9047a1
data/README.md CHANGED
@@ -11,24 +11,25 @@ It's a small gem providing **algebraic types** and **pattern matching** on them
11
11
  ## What is it good for?
12
12
 
13
13
  - Defining data structures.
14
- - Algebraic types play nice with JSON serialization and deserialization. It is ideal for defining
15
- message-based cross-process communication.
14
+ - Algebraic types play nice with JSON serialization and deserialization. It is ideal for defining message-based cross-process communication.
16
15
  - and more...
17
16
 
18
17
  ## Quick example
19
18
 
20
- Load DSL for type definition and define some algebraic types
19
+ Let's define a Tree
21
20
 
22
21
  ```ruby
23
- extend Algebrick::DSL
22
+ Tree = Algebrick.type do |tree|
23
+ Empty = type
24
+ Leaf = type { fields Integer }
25
+ Node = type { fields tree, tree }
24
26
 
25
- type_def do
26
- tree === empty | leaf(Integer) | node(tree, tree)
27
+ variants Empty, Leaf, Node
27
28
  end
28
29
  ```
29
30
 
30
31
  Now types `Tree(Empty | Leaf | Node)`, `Empty`, `Leaf(Integer)` and `Node(Tree, Tree)` are defined.
31
- Lets add some methods, don't miss the **pattern matching** example.
32
+ Add some methods, don't miss the **pattern matching** example.
32
33
 
33
34
  ```ruby
34
35
  module Tree
data/README_FULL.md CHANGED
@@ -26,14 +26,14 @@ Same thing can be defined with this gem:
26
26
 
27
27
  {include:file:doc/tree1.out.rb}
28
28
 
29
- There are 4 kinds of algebraic types in Algebrick gem:
29
+ There are 4 kinds of algebraic types:
30
30
 
31
- - **Atom** a type that has only one value e.g. `Empty`.
32
- - **Product** a type that has a set nuber of fields with given type e.g. `Leaf(Integer)`
33
- - **Variant** a type that does have set number of variants e.g. `Tree(Empty | Leaf(Integer) | Node(Tree, Tree)`.
34
- It means that values of `Empty`, `Leaf[1]`, `Node[Empty, Empry]` have all type `Tree`.
35
- - **ProductVariant** will be created when a recursive type like `list === empty | list(Object, list)` is defined.
36
- `List` has two variants `Empty` and itself simultaneously it has fields as product type.
31
+ 1. **Atom** a type that has only one value e.g. `Empty`.
32
+ 2. **Product** a type that has a set number of fields with given type e.g. `Leaf(Integer)`
33
+ 3. **Variant** a type that does have set number of variants e.g. `Tree(Empty | Leaf(Integer) | Node(Tree, Tree)`. It means that values of `Empty`, `Leaf[1]`, `Node[Empty, Empry]` are all of type `Tree`.
34
+ 4. **ProductVariant** will be created when a recursive type like `List(Empty | List(Integer, List))` is defined. `List` has two variants `Empty` and itself, and simultaneously it has fields as product type.
35
+
36
+ Atom type is implemented with {Algebrick::Atom} and the rest is implemented with {Algebrick::ProductVariant} which behaves differently based on what is set: fields, variants or both.
37
37
 
38
38
  ### Type definition
39
39
 
@@ -49,7 +49,7 @@ There are 4 kinds of algebraic types in Algebrick gem:
49
49
 
50
50
  ### Pattern matching
51
51
 
52
- Algebraic matchers are helper objects to match algebraic objects and others with
52
+ Algebraic matchers are helper objects to match algebraic values and others with
53
53
  `#===` method based on theirs initialization values.
54
54
 
55
55
  {include:file:doc/pattern_matching.out.rb}
@@ -88,7 +88,7 @@ Just small snippet from a gem I am still working on.
88
88
 
89
89
  def on_message(message)
90
90
  match message,
91
- Work.(~any, ~any) --> actor, work do
91
+ Work.(~any, ~any) >-> actor, work do
92
92
  @executor.tell Finished[actor, work.call, self.reference]
93
93
  end
94
94
  end
@@ -97,6 +97,5 @@ Just small snippet from a gem I am still working on.
97
97
  ### TODO
98
98
 
99
99
  - Menu model, TypedArray
100
- - Pretty print example, see {http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf}
101
100
  - update actor pattern when gem is done
102
-
101
+ - example with birth-number Valid|Invalid
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.3
1
+ 0.2.0
data/lib/algebrick.rb CHANGED
@@ -18,14 +18,14 @@
18
18
 
19
19
  require 'set'
20
20
 
21
- class Module
22
- # Return any modules we +extend+
23
- def extended_modules
24
- class << self
25
- self
26
- end.included_modules
27
- end
28
- end
21
+ #class Module
22
+ # # Return any modules we +extend+
23
+ # def extended_modules
24
+ # class << self
25
+ # self
26
+ # end.included_modules
27
+ # end
28
+ #end
29
29
 
30
30
  module Algebrick
31
31
 
@@ -78,8 +78,8 @@ module Algebrick
78
78
  alias_method :_, :any # TODO make it optional
79
79
 
80
80
  #match Empty,
81
- # Empty -->() {},
82
- # Leaf.(~any) >>-> value do
81
+ # Empty >> static_value_like_string,
82
+ # Leaf.(~any) >-> value do
83
83
  # value
84
84
  # end
85
85
  #match Empty,
@@ -139,6 +139,10 @@ module Algebrick
139
139
  to_m | other
140
140
  end
141
141
 
142
+ def ^(other)
143
+ to_m ^ other
144
+ end
145
+
142
146
  def !
143
147
  !to_m
144
148
  end
@@ -154,6 +158,10 @@ module Algebrick
154
158
  def >>(block)
155
159
  to_m >> block
156
160
  end
161
+
162
+ def >(block)
163
+ to_m > block
164
+ end
157
165
  end
158
166
 
159
167
  class Type < Module
@@ -161,6 +169,15 @@ module Algebrick
161
169
  include Matching
162
170
  include MatcherDelegations
163
171
 
172
+ def initialize(name, &definition)
173
+ super &definition
174
+ @name = name
175
+ end
176
+
177
+ def name
178
+ super || @name
179
+ end
180
+
164
181
  def to_m(*args)
165
182
  raise NotImplementedError
166
183
  end
@@ -213,8 +230,8 @@ module Algebrick
213
230
  class Atom < Type
214
231
  include Value
215
232
 
216
- def initialize(&block)
217
- super &block
233
+ def initialize(name, &block)
234
+ super name, &block
218
235
  extend self
219
236
  end
220
237
 
@@ -316,55 +333,11 @@ module Algebrick
316
333
  end
317
334
  end
318
335
 
319
- class AbstractProductVariant < Type
320
- def be_kind_of(type = nil)
321
- if initialized?
322
- be_kind_of! type if type
323
- if @to_be_kind_of
324
- while (type = @to_be_kind_of.shift)
325
- be_kind_of! type
326
- end
327
- end
328
- else
329
- @to_be_kind_of ||= []
330
- @to_be_kind_of << type if type
331
- end
332
- self
333
- end
334
-
335
- def add_field_method_accessor(field)
336
- raise TypeError, 'no field names' unless @field_names
337
- raise TypeError, "no field name #{field}" unless @field_names.include? field
338
- define_method(field) { self[field] }
339
- end
340
-
341
- def add_field_method_accessors(*fields)
342
- fields.each { |f| add_field_method_accessor f }
343
- end
344
-
345
- def add_all_field_method_accessors
346
- add_field_method_accessors *@field_names
347
- end
348
-
349
- protected
350
-
351
- def be_kind_of!(type)
352
- raise NotImplementedError
353
- end
354
-
355
- private
356
-
357
- def initialized?
358
- !!@initialized
359
- end
360
-
361
- def initialize(&block)
362
- super &block
363
- @initialized = true
364
- be_kind_of
365
- end
336
+ class ProductVariant < Type
337
+ attr_reader :fields, :field_names, :field_indexes, :variants
366
338
 
367
339
  def set_fields(fields_or_hash)
340
+ raise TypeError, 'can be set only once' if @fields
368
341
  fields, keys = if fields_or_hash.size == 1 && fields_or_hash.first.is_a?(Hash)
369
342
  [fields_or_hash.first.values, fields_or_hash.first.keys]
370
343
  else
@@ -380,16 +353,25 @@ module Algebrick
380
353
  @constructor = Class.new(ProductConstructor).tap { |c| c.type = self }
381
354
  end
382
355
 
383
- def set_field_names(names)
384
- @field_names = names
385
- names.all? { |k| is_kind_of! k, Symbol }
386
- dict = @field_indexes =
387
- Hash.new { |h, k| raise ArgumentError, "uknown field #{k.inspect}" }.
388
- update names.each_with_index.inject({}) { |h, (k, i)| h.update k => i }
389
- define_method(:[]) { |key| @fields[dict[key]] }
356
+ def add_field_method_accessor(field)
357
+ raise TypeError, 'no field names' unless @field_names
358
+ raise ArgumentError, "no field name #{field}" unless @field_names.include? field
359
+ raise ArgumentError, "method #{field} already defined" if instance_methods.include? field
360
+ define_method(field) { self[field] }
361
+ self
362
+ end
363
+
364
+ def add_field_method_accessors(*fields)
365
+ fields.each { |f| add_field_method_accessor f }
366
+ self
367
+ end
368
+
369
+ def add_all_field_method_accessors
370
+ add_field_method_accessors *@field_names
390
371
  end
391
372
 
392
373
  def set_variants(variants)
374
+ raise TypeError, 'can be set only once' if @variants
393
375
  variants.all? { |v| is_kind_of! v, Type, Class }
394
376
  @variants = variants
395
377
  variants.each do |v|
@@ -401,14 +383,91 @@ module Algebrick
401
383
  end
402
384
  end
403
385
 
404
- def product_be_kind_of(type)
405
- @constructor.send :include, type
386
+ def new(*fields)
387
+ raise TypeError unless @constructor
388
+ @constructor.new *fields
406
389
  end
407
390
 
408
- def construct_product(*fields)
409
- @constructor.new *fields
391
+ alias_method :[], :new
392
+
393
+ def ==(other)
394
+ other.kind_of? ProductVariant and
395
+ variants == other.variants and fields == other.fields
410
396
  end
411
397
 
398
+ def be_kind_of(type)
399
+ kind
400
+ @constructor.send :include, type if @constructor
401
+ variants.each { |v| v.be_kind_of type unless v == self } if @variants
402
+ end
403
+
404
+ def call(*field_matchers)
405
+ raise TypeError unless @fields
406
+ Matchers::Product.new self, *field_matchers
407
+ end
408
+
409
+ def to_m
410
+ case kind
411
+ when :product
412
+ Matchers::Product.new self
413
+ when :product_variant
414
+ Matchers::Variant.new self
415
+ when :variant
416
+ Matchers::Variant.new self
417
+ else
418
+ raise
419
+ end
420
+ end
421
+
422
+ def to_s
423
+ case kind
424
+ when :product
425
+ product_to_s
426
+ when :product_variant
427
+ name + '(' +
428
+ variants.map do |variant|
429
+ if variant == self
430
+ product_to_s
431
+ else
432
+ variant.name
433
+ end
434
+ end.join(' | ') +
435
+ ')'
436
+ when :variant
437
+ "#{name}(#{variants.map(&:name).join ' | '})"
438
+ else
439
+ raise
440
+ end
441
+ end
442
+
443
+ def from_hash(hash)
444
+ case kind
445
+ when :product
446
+ product_from_hash hash
447
+ when :product_variant
448
+ product_from_hash hash
449
+ when :variant
450
+ field_from_hash hash
451
+ else
452
+ raise
453
+ end
454
+ end
455
+
456
+ def kind
457
+ case
458
+ when @fields && !@variants
459
+ :product
460
+ when @fields && @variants
461
+ :product_variant
462
+ when !@fields && @variants
463
+ :variant
464
+ when !@fields && !@variants
465
+ raise TypeError, 'fields or variants have to be set'
466
+ end
467
+ end
468
+
469
+ private
470
+
412
471
  def product_to_s
413
472
  fields_str = if field_names
414
473
  field_names.zip(fields).map { |name, field| "#{name}: #{field.name}" }
@@ -418,6 +477,15 @@ module Algebrick
418
477
  "#{name}(#{fields_str.join ', '})"
419
478
  end
420
479
 
480
+ def set_field_names(names)
481
+ @field_names = names
482
+ names.all? { |k| is_kind_of! k, Symbol }
483
+ dict = @field_indexes =
484
+ Hash.new { |h, k| raise ArgumentError, "unknown field #{k.inspect} in #{self}" }.
485
+ update names.each_with_index.inject({}) { |h, (k, i)| h.update k => i }
486
+ define_method(:[]) { |key| @fields[dict[key]] }
487
+ end
488
+
421
489
  def product_from_hash(hash)
422
490
  (type_name = hash[TYPE_KEY] || hash[TYPE_KEY.to_s]) or
423
491
  raise ArgumentError, "hash does not have #{TYPE_KEY}"
@@ -457,122 +525,35 @@ module Algebrick
457
525
  end
458
526
  end
459
527
 
460
- class Product < AbstractProductVariant
461
- attr_reader :fields, :field_names, :field_indexes
462
-
463
- def initialize(*fields, &block)
464
- set_fields fields
465
- super(&block)
466
- end
467
-
468
- def new(*fields)
469
- construct_product(*fields)
470
- end
471
-
472
- alias_method :[], :new
473
-
474
- def be_kind_of!(type)
475
- product_be_kind_of type
476
- end
477
-
478
- def call(*field_matchers)
479
- Matchers::Product.new self, *field_matchers
480
- end
481
-
482
- def to_m
483
- call *::Array.new(fields.size) { Algebrick.any }
484
- end
528
+ class TypeDefinitionScope
529
+ attr_reader :new_type
485
530
 
486
- def ==(other)
487
- other.kind_of? Product and fields == other.fields
488
- end
489
-
490
- def to_s
491
- product_to_s
492
- end
493
-
494
- def from_hash(hash)
495
- product_from_hash hash
496
- end
497
- end
498
-
499
- class Variant < AbstractProductVariant
500
- attr_reader :variants
501
-
502
- def initialize(*variants, &block)
503
- set_variants(variants)
504
- super &block
505
- end
506
-
507
- def be_kind_of!(type)
508
- variants.each { |v| v.be_kind_of type }
509
- end
510
-
511
- def to_m
512
- Matchers::Variant.new self
531
+ def initialize(&block)
532
+ @new_type = ProductVariant.new nil
533
+ instance_exec @new_type, &block
534
+ @new_type.kind
513
535
  end
514
536
 
515
- def ==(other)
516
- other.kind_of? Variant and variants == other.variants
537
+ def fields(*fields)
538
+ @new_type.set_fields fields
539
+ self
517
540
  end
518
541
 
519
- def to_s
520
- "#{name}(#{variants.map(&:name).join ' | '})"
542
+ def variants(*variants)
543
+ @new_type.set_variants variants
544
+ self
521
545
  end
522
546
 
523
- def from_hash(hash)
524
- field_from_hash hash
547
+ def type(&block)
548
+ Algebrick.type &block
525
549
  end
526
550
  end
527
551
 
528
- class ProductVariant < AbstractProductVariant
529
- attr_reader :fields, :field_names, :field_indexes, :variants
530
-
531
- def initialize(fields, variants, &block)
532
- set_fields fields
533
- raise unless variants.include? self
534
- set_variants variants
535
- super &block
536
- end
537
-
538
- def be_kind_of!(type)
539
- variants.each { |v| v.be_kind_of type unless v == self }
540
- product_be_kind_of type
541
- end
542
-
543
- def call(*field_matchers)
544
- Matchers::Product.new self, *field_matchers
545
- end
546
-
547
- def to_m
548
- Matchers::Variant.new self
549
- end
550
-
551
- def new(*fields)
552
- construct_product(*fields)
553
- end
554
-
555
- alias_method :[], :new
556
-
557
- def ==(other)
558
- other.kind_of? ProductVariant and
559
- variants == other.variants and fields == other.fields
560
- end
561
-
562
- def to_s
563
- name + '(' +
564
- variants.map do |variant|
565
- if variant == self
566
- product_to_s
567
- else
568
- variant.name
569
- end
570
- end.join(' | ') +
571
- ')'
572
- end
573
-
574
- def from_hash(hash)
575
- product_from_hash hash
552
+ def self.type(&block)
553
+ if block.nil?
554
+ Atom.new nil
555
+ else
556
+ TypeDefinitionScope.new(&block).new_type
576
557
  end
577
558
  end
578
559
 
@@ -583,26 +564,27 @@ module Algebrick
583
564
  attr_reader :value
584
565
 
585
566
  def initialize
586
- @assign, @value = nil
567
+ @assign, @value, @matched = nil
587
568
  end
588
569
 
589
570
  def case(&block)
590
571
  return self, block
591
572
  end
592
573
 
574
+ raise 'remove deprecation' if Algebrick.version >= Gem::Version.new('0.3')
575
+
593
576
  def -(block)
594
- return self, block
577
+ warn "a 'matcher --> {}' and 'matcher +-> {}' is deprecated, it'll be removed in 0.3\n#{caller[0]}"
578
+ self > block
595
579
  end
596
580
 
597
- alias_method :>>, :-
598
-
599
- raise 'remove deprecation' if Algebrick.version >= Gem::Version.new('0.2')
600
-
601
- def +(block)
602
- warn 'a_matcher +-> {} is deprecated, it\'ll be removed in 0.2'
603
- self - block
581
+ def >(block)
582
+ return self, block
604
583
  end
605
584
 
585
+ alias_method :>>, :>
586
+ alias_method :+, :-
587
+
606
588
  def ~
607
589
  @assign = true
608
590
  self
@@ -620,16 +602,23 @@ module Algebrick
620
602
  Not.new self
621
603
  end
622
604
 
605
+ def ^(matcher)
606
+ Xor.new self, matcher
607
+ end
608
+
623
609
  def assign?
624
610
  @assign
625
611
  end
626
612
 
613
+ def matched?
614
+ @matched
615
+ end
616
+
627
617
  def children_including_self
628
618
  children.unshift self
629
619
  end
630
620
 
631
621
  def assigns
632
- mine = @assign && @value ? [@value] : []
633
622
  mine = @assign ? [@value] : []
634
623
  children.inject(mine) { |assigns, child| assigns + child.assigns }.tap do
635
624
  return yield *assigns if block_given?
@@ -637,7 +626,7 @@ module Algebrick
637
626
  end
638
627
 
639
628
  def ===(other)
640
- matching?(other).tap { |matched| @value = other if matched }
629
+ matching?(other).tap { |matched| @value = other if (@matched = matched) }
641
630
  end
642
631
 
643
632
  def assign_to_s
@@ -725,6 +714,33 @@ module Algebrick
725
714
  end
726
715
  end
727
716
 
717
+ class Xor < Or
718
+ def to_s
719
+ matchers.join ' ^ '
720
+ end
721
+
722
+ alias_method :super_children, :children
723
+ private :super_children
724
+
725
+ def children
726
+ super.select &:matched?
727
+ end
728
+
729
+ def assigns
730
+ super.tap do |assigns|
731
+ missing = assigns_size - assigns.size
732
+ assigns.push(*::Array.new(missing))
733
+ end
734
+ end
735
+
736
+ private
737
+
738
+ def assigns_size
739
+ # TODO is it efficient?
740
+ super_children.map { |ch| ch.assigns.size }.max
741
+ end
742
+ end
743
+
728
744
  class Not < Abstract
729
745
  attr_reader :matcher
730
746
 
@@ -854,13 +870,14 @@ module Algebrick
854
870
  # TODO Method matcher (:size, matcher)
855
871
 
856
872
  class Product < Abstract
873
+ # TODO allow to match by field_name e.g. Address.(:street)
857
874
  attr_reader :algebraic_type, :field_matchers
858
875
 
859
876
  def initialize(algebraic_type, *field_matchers)
860
877
  super()
861
- is_kind_of! algebraic_type, Algebrick::Product, Algebrick::ProductVariant
862
- @algebraic_type = algebraic_type
863
- field_matchers += ::Array.new(algebraic_type.fields.size) { Algebrick.any } if field_matchers.empty?
878
+ @algebraic_type = is_kind_of! algebraic_type, Algebrick::ProductVariant
879
+ raise ArgumentError unless algebraic_type.fields
880
+ field_matchers += ::Array.new(algebraic_type.fields.size) { Algebrick.any } if field_matchers.empty?
864
881
  @field_matchers = field_matchers
865
882
  raise ArgumentError unless algebraic_type.fields.size == field_matchers.size
866
883
  end
@@ -891,7 +908,8 @@ module Algebrick
891
908
 
892
909
  class Variant < Wrapper
893
910
  def initialize(something)
894
- is_kind_of! something, Algebrick::Variant, Algebrick::ProductVariant
911
+ raise ArgumentError unless something.variants
912
+ is_kind_of! something, Algebrick::ProductVariant
895
913
  super something
896
914
  end
897
915
 
@@ -912,161 +930,565 @@ module Algebrick
912
930
  end
913
931
  end
914
932
 
915
- module DSL
916
- class PreType
917
- attr_reader :environment, :name, :fields, :variants, :definition
918
-
919
- def initialize(environment, name)
920
- @environment = environment
921
- @name = name
922
- @fields = []
923
- @variants = nil
924
- @definition = nil
925
- end
926
-
927
- def |(other)
928
- [self, other]
929
- end
930
-
931
- def to_ary
932
- [self]
933
- end
934
-
935
- def fields=(fields)
936
- raise unless @fields.empty?
937
- @fields += fields
938
- end
939
-
940
- def definition=(block)
941
- raise if @definition
942
- @definition = block
943
- end
944
-
945
- def is(variants)
946
- raise if @variants
947
- @variants = variants
948
- self
949
- end
950
-
951
- alias_method :===, :is
952
-
953
- def kind
954
- if @variants
955
- if @fields.empty?
956
- Variant
957
- else
958
- ProductVariant
959
- end
960
- else
961
- if @fields.empty?
962
- Atom
963
- else
964
- Product
965
- end
966
- end
967
- end
968
- end
969
-
970
- class Environment
971
- attr_reader :pre_types
972
- def initialize(base, &definition)
973
- @base = if base.is_a?(Object) && base.to_s == 'main'
974
- Object
975
- else
976
- base
977
- end
978
- @pre_types = {}
979
- instance_eval &definition
980
- end
981
-
982
- def method_missing(method, *fields, &definition)
983
- const_name = method.to_s.split('_').map { |s| s[0] = s[0].upcase; s }.join
984
-
985
- @pre_types[const_name] ||= PreType.new(self, const_name)
986
- @pre_types[const_name].fields = fields unless fields.empty?
987
- @pre_types[const_name].definition = definition if definition
988
- @pre_types[const_name]
989
- end
990
-
991
- def run
992
- define_constants
993
- define_fields_and_variants
994
- eval_definitions
995
- @pre_types.map { |name, _| get_class name }
996
- end
997
-
998
- private
999
-
1000
- def define_constants
1001
- @pre_types.each do |name, pre_type|
1002
- type = pre_type.kind.allocate
1003
- if @base.const_defined? name
1004
- defined = @base.const_get(name)
1005
- # #unless defined == type
1006
- raise "#{name} already defined as #{defined}"
1007
- # #end
1008
- else
1009
- #puts "defining #{name.to_sym.inspect} in #{@base}"
1010
- @base.const_set name.to_sym, type
1011
- end
1012
- end
1013
- end
1014
-
1015
- def define_fields_and_variants
1016
- select = ->(klass, &block) do
1017
- @pre_types.select { |_, pre_type| pre_type.kind == klass }.
1018
- map { |name, pre_type| [name, get_class(name), pre_type] }.
1019
- each &block
1020
- end
1021
-
1022
- select.(Atom) do |name, type, pre_type|
1023
- type.send :initialize
1024
- end
1025
-
1026
- select.(Product) do |name, type, pre_type|
1027
- type.send :initialize, *pre_type.fields.map { |f| get_class f }
1028
- end
1029
-
1030
- select.(Variant) do |name, type, pre_type|
1031
- type.send :initialize, *pre_type.variants.map { |v| get_class v }
1032
- end
1033
-
1034
- select.(ProductVariant) do |name, type, pre_type|
1035
- type.send :initialize,
1036
- pre_type.fields.map { |f| get_class f },
1037
- pre_type.variants.map { |v| get_class v }
1038
- end
1039
- end
1040
-
1041
- def eval_definitions
1042
- @pre_types.each do |name, pre_type|
1043
- next unless pre_type.definition
1044
- type = get_class name
1045
- type.module_eval &pre_type.definition
1046
- end
1047
- end
1048
-
1049
- def get_class(key)
1050
- if key.kind_of? String
1051
- @base.const_get key
1052
- elsif key.kind_of? PreType
1053
- @base.const_get key.name
1054
- elsif key.kind_of? Hash
1055
- key.each { |k, v| key[k] = get_class v }
1056
- else
1057
- key
1058
- end
1059
- end
1060
- end
1061
-
1062
- def type_def(base = self, &definition)
1063
- Environment.new(base, &definition).run
1064
- end
1065
- end
1066
-
1067
- extend DSL
1068
-
1069
- def self.type_def(base, &definition)
1070
- super base, &definition
1071
- end
933
+ #class AbstractProductVariant < Type
934
+ # def be_kind_of(type = nil)
935
+ # if initialized?
936
+ # be_kind_of! type if type
937
+ # if @to_be_kind_of
938
+ # while (type = @to_be_kind_of.shift)
939
+ # be_kind_of! type
940
+ # end
941
+ # end
942
+ # else
943
+ # @to_be_kind_of ||= []
944
+ # @to_be_kind_of << type if type
945
+ # end
946
+ # self
947
+ # end
948
+ #
949
+ # def add_field_method_accessor(field)
950
+ # raise TypeError, 'no field names' unless @field_names
951
+ # raise TypeError, "no field name #{field}" unless @field_names.include? field
952
+ # define_method(field) { self[field] }
953
+ # self
954
+ # end
955
+ #
956
+ # def add_field_method_accessors(*fields)
957
+ # fields.each { |f| add_field_method_accessor f }
958
+ # self
959
+ # end
960
+ #
961
+ # def add_all_field_method_accessors
962
+ # add_field_method_accessors *@field_names
963
+ # end
964
+ #
965
+ # protected
966
+ #
967
+ # def be_kind_of!(type)
968
+ # raise NotImplementedError
969
+ # end
970
+ #
971
+ # private
972
+ #
973
+ # def initialized?
974
+ # !!@initialized
975
+ # end
976
+ #
977
+ # def initialize(name, &block)
978
+ # super name, &block
979
+ # @initialized = true
980
+ # be_kind_of
981
+ # end
982
+ #
983
+ # def set_fields(fields_or_hash)
984
+ # fields, keys = if fields_or_hash.size == 1 && fields_or_hash.first.is_a?(Hash)
985
+ # [fields_or_hash.first.values, fields_or_hash.first.keys]
986
+ # else
987
+ # [fields_or_hash, nil]
988
+ # end
989
+ #
990
+ # set_field_names keys if keys
991
+ #
992
+ # fields.all? { |f| is_kind_of! f, Type, Class }
993
+ # raise TypeError, 'there is no product with zero fields' unless fields.size > 0
994
+ # define_method(:value) { @fields.first } if fields.size == 1
995
+ # @fields = fields
996
+ # @constructor = Class.new(ProductConstructor).tap { |c| c.type = self }
997
+ # end
998
+ #
999
+ # def set_field_names(names)
1000
+ # @field_names = names
1001
+ # names.all? { |k| is_kind_of! k, Symbol }
1002
+ # dict = @field_indexes =
1003
+ # Hash.new { |h, k| raise ArgumentError, "unknown field #{k.inspect} in #{self}" }.
1004
+ # update names.each_with_index.inject({}) { |h, (k, i)| h.update k => i }
1005
+ # define_method(:[]) { |key| @fields[dict[key]] }
1006
+ # end
1007
+ #
1008
+ # def set_variants(variants)
1009
+ # variants.all? { |v| is_kind_of! v, Type, Class }
1010
+ # @variants = variants
1011
+ # variants.each do |v|
1012
+ # if v.respond_to? :be_kind_of
1013
+ # v.be_kind_of self
1014
+ # else
1015
+ # v.send :include, self
1016
+ # end
1017
+ # end
1018
+ # end
1019
+ #
1020
+ # def product_be_kind_of(type)
1021
+ # @constructor.send :include, type
1022
+ # end
1023
+ #
1024
+ # def construct_product(*fields)
1025
+ # @constructor.new *fields
1026
+ # end
1027
+ #
1028
+ # def product_to_s
1029
+ # fields_str = if field_names
1030
+ # field_names.zip(fields).map { |name, field| "#{name}: #{field.name}" }
1031
+ # else
1032
+ # fields.map(&:name)
1033
+ # end
1034
+ # "#{name}(#{fields_str.join ', '})"
1035
+ # end
1036
+ #
1037
+ # def product_from_hash(hash)
1038
+ # (type_name = hash[TYPE_KEY] || hash[TYPE_KEY.to_s]) or
1039
+ # raise ArgumentError, "hash does not have #{TYPE_KEY}"
1040
+ # raise ArgumentError, "#{type_name} is not #{name}" unless type_name == name
1041
+ #
1042
+ # fields = hash[FIELDS_KEY] || hash[FIELDS_KEY.to_s] ||
1043
+ # hash.reject { |k, _| k.to_s == TYPE_KEY.to_s }
1044
+ # is_kind_of! fields, Hash, Array
1045
+ #
1046
+ # case fields
1047
+ # when Array
1048
+ # self[*fields.map { |value| field_from_hash value }]
1049
+ # when Hash
1050
+ # self[fields.inject({}) do |h, (name, value)|
1051
+ # raise ArgumentError unless @field_names.map(&:to_s).include? name.to_s
1052
+ # h.update name.to_sym => field_from_hash(value)
1053
+ # end]
1054
+ # end
1055
+ # end
1056
+ #
1057
+ # def field_from_hash(hash)
1058
+ # return hash unless Hash === hash
1059
+ # (type_name = hash[TYPE_KEY] || hash[TYPE_KEY.to_s]) or return hash
1060
+ # type = constantize type_name
1061
+ # type.from_hash hash
1062
+ # end
1063
+ #
1064
+ # def constantize(camel_cased_word)
1065
+ # names = camel_cased_word.split('::')
1066
+ # names.shift if names.empty? || names.first.empty?
1067
+ #
1068
+ # constant = Object
1069
+ # names.each do |name|
1070
+ # constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
1071
+ # end
1072
+ # constant
1073
+ # end
1074
+ #end
1075
+ #
1076
+ #class Product < AbstractProductVariant
1077
+ # attr_reader :fields, :field_names, :field_indexes
1078
+ #
1079
+ # def initialize(name, *fields, &block)
1080
+ # set_fields fields
1081
+ # super(name, &block)
1082
+ # end
1083
+ #
1084
+ # def new(*fields)
1085
+ # construct_product(*fields)
1086
+ # end
1087
+ #
1088
+ # alias_method :[], :new
1089
+ #
1090
+ # def be_kind_of!(type)
1091
+ # product_be_kind_of type
1092
+ # end
1093
+ #
1094
+ # def call(*field_matchers)
1095
+ # Matchers::Product.new self, *field_matchers
1096
+ # end
1097
+ #
1098
+ # def to_m
1099
+ # call *::Array.new(fields.size) { Algebrick.any }
1100
+ # end
1101
+ #
1102
+ # def ==(other)
1103
+ # other.kind_of? Product and fields == other.fields
1104
+ # end
1105
+ #
1106
+ # def to_s
1107
+ # product_to_s
1108
+ # end
1109
+ #
1110
+ # def from_hash(hash)
1111
+ # product_from_hash hash
1112
+ # end
1113
+ #end
1114
+ #
1115
+ #class Variant < AbstractProductVariant
1116
+ # attr_reader :variants
1117
+ #
1118
+ # def initialize(name, *variants, &block)
1119
+ # set_variants(variants)
1120
+ # super name, &block
1121
+ # end
1122
+ #
1123
+ # def be_kind_of!(type)
1124
+ # variants.each { |v| v.be_kind_of type }
1125
+ # end
1126
+ #
1127
+ # def to_m
1128
+ # Matchers::Variant.new self
1129
+ # end
1130
+ #
1131
+ # def ==(other)
1132
+ # other.kind_of? Variant and variants == other.variants
1133
+ # end
1134
+ #
1135
+ # def to_s
1136
+ # "#{name}(#{variants.map(&:name).join ' | '})"
1137
+ # end
1138
+ #
1139
+ # def from_hash(hash)
1140
+ # field_from_hash hash
1141
+ # end
1142
+ #end
1143
+ #
1144
+ #class ProductVariant < AbstractProductVariant
1145
+ # attr_reader :fields, :field_names, :field_indexes, :variants
1146
+ #
1147
+ # def initialize(name, fields, variants, &block)
1148
+ # set_fields fields
1149
+ # raise unless variants.include? self
1150
+ # set_variants variants
1151
+ # super name, &block
1152
+ # end
1153
+ #
1154
+ # def be_kind_of!(type)
1155
+ # variants.each { |v| v.be_kind_of type unless v == self }
1156
+ # product_be_kind_of type
1157
+ # end
1158
+ #
1159
+ # def call(*field_matchers)
1160
+ # Matchers::Product.new self, *field_matchers
1161
+ # end
1162
+ #
1163
+ # def to_m
1164
+ # Matchers::Variant.new self
1165
+ # end
1166
+ #
1167
+ # def new(*fields)
1168
+ # construct_product(*fields)
1169
+ # end
1170
+ #
1171
+ # alias_method :[], :new
1172
+ #
1173
+ # def ==(other)
1174
+ # other.kind_of? ProductVariant and
1175
+ # variants == other.variants and fields == other.fields
1176
+ # end
1177
+ #
1178
+ # def to_s
1179
+ # name + '(' +
1180
+ # variants.map do |variant|
1181
+ # if variant == self
1182
+ # product_to_s
1183
+ # else
1184
+ # variant.name
1185
+ # end
1186
+ # end.join(' | ') +
1187
+ # ')'
1188
+ # end
1189
+ #
1190
+ # def from_hash(hash)
1191
+ # product_from_hash hash
1192
+ # end
1193
+ #end
1194
+
1195
+ #module DSL
1196
+ # -> do
1197
+ # maybe[:a] === none | some(:a)
1198
+ # tree[:a] === tip | tree(:a, tree, tree)
1199
+ # end
1200
+ #
1201
+ # -> do
1202
+ # maybe[:a].can_be none,
1203
+ # some.has(:a)
1204
+ #
1205
+ # maybe[:a].can_be none,
1206
+ # some.having(:a)
1207
+ #
1208
+ # maybe[:a].can_be none,
1209
+ # some.having(:a)
1210
+ # tree.can_be tip,
1211
+ # tree.having(Object, tree, tree)
1212
+ #
1213
+ # tree.can_be empty,
1214
+ # leaf.having(Object),
1215
+ # node.having(left: tree, right: tree)
1216
+ #
1217
+ # tree do
1218
+ # # def ...
1219
+ # end
1220
+ # end
1221
+ #
1222
+ # class PreType
1223
+ # include TypeCheck
1224
+ #
1225
+ # attr_reader :environment, :name, :fields, :variants, :definition, :variables
1226
+ #
1227
+ # def initialize(environment, name)
1228
+ # @environment = is_kind_of! environment, Environment
1229
+ # @name = is_kind_of! name, String
1230
+ # @fields = []
1231
+ # @variables = []
1232
+ # @variants = []
1233
+ # @definition = nil
1234
+ # end
1235
+ #
1236
+ # def const_name
1237
+ # @const_name ||= name.to_s.split('_').map { |s| s.tap { s[0] = s[0].upcase } }.join
1238
+ # end
1239
+ #
1240
+ # #def |(other)
1241
+ # # [self, other]
1242
+ # #end
1243
+ # #
1244
+ # #def to_ary
1245
+ # # [self]
1246
+ # #end
1247
+ #
1248
+ # def [](*variables)
1249
+ # variables.all? { |var| is_kind_of! var, Symbol }
1250
+ # @variables += variables
1251
+ # self
1252
+ # end
1253
+ #
1254
+ # def fields=(fields)
1255
+ # raise 'fields can be defined only once' unless @fields.empty?
1256
+ # fields.each do |field|
1257
+ # if Hash === field
1258
+ # field.each do |k, v|
1259
+ # is_kind_of! k, Symbol
1260
+ # is_kind_of! v, PreType, Type, Class, Symbol
1261
+ # @variables.push v if v.is_a? Symbol
1262
+ # end
1263
+ # else
1264
+ # is_kind_of! field, PreType, Type, Class, Symbol
1265
+ # @variables += fields.select { |v| v.is_a? Symbol }
1266
+ # end
1267
+ # end
1268
+ # @fields += fields
1269
+ # end
1270
+ #
1271
+ # def having(fields)
1272
+ # self.fields = fields
1273
+ # self
1274
+ # end
1275
+ #
1276
+ # alias_method :has, :having
1277
+ #
1278
+ # def fields_array
1279
+ # if (hash = @fields.find { |f| Hash === f })
1280
+ # hash.values
1281
+ # else
1282
+ # @fields
1283
+ # end
1284
+ # end
1285
+ #
1286
+ # #def dependent(set = Set.new)
1287
+ # # children = @variants + @fields
1288
+ # # children.each do |a_type|
1289
+ # # next if set.include? a_type
1290
+ # # next unless a_type.respond_to? :dependent
1291
+ # # set.add a_type
1292
+ # # a_type.dependent set
1293
+ # # end
1294
+ # # set.to_a
1295
+ # #end
1296
+ #
1297
+ # def definition=(block)
1298
+ # raise 'definition can be defined only once' if @definition
1299
+ # @definition = block
1300
+ # end
1301
+ #
1302
+ # def can_be(*variants)
1303
+ # raise 'variants can be defined only once' unless @variants.empty?
1304
+ # @variants = variants
1305
+ # self
1306
+ # end
1307
+ #
1308
+ # def no_variables?
1309
+ # !variables? and fields_array.select { |f| Symbol === f }.empty?
1310
+ # end
1311
+ #
1312
+ # def variables?
1313
+ # not @variables.empty?
1314
+ # end
1315
+ #
1316
+ # def kind
1317
+ # unless @variants.empty?
1318
+ # if @fields.empty?
1319
+ # Variant
1320
+ # else
1321
+ # ProductVariant
1322
+ # end
1323
+ # else
1324
+ # if @fields.empty?
1325
+ # Atom
1326
+ # else
1327
+ # Product
1328
+ # end
1329
+ # end
1330
+ # end
1331
+ # end
1332
+ #
1333
+ # class TypeConstructor
1334
+ # include TypeCheck
1335
+ # attr_reader :const_name
1336
+ #
1337
+ # def initialize(pre_types, const_name, variables)
1338
+ # warn "types with variables are experimental\n#{caller[0]}"
1339
+ # @pre_types = is_kind_of! pre_types, Hash
1340
+ # @const_name = is_kind_of! const_name, String
1341
+ # @variables = is_kind_of! variables, Array
1342
+ # @cache = {}
1343
+ # end
1344
+ #
1345
+ # def [](*variables)
1346
+ # variables.size == @variables.size or
1347
+ # raise ArgumentError, "variables size differs from #{@variables}"
1348
+ # @cache[variables] ||= begin
1349
+ #
1350
+ # TypeFactory.new(@pre_types, Hash[@variables.zip(variables)]).define.tap do |types|
1351
+ # types.each do |name, type|
1352
+ #
1353
+ # end
1354
+ # end
1355
+ # end
1356
+ # end
1357
+ #
1358
+ # def to_s
1359
+ # const_name + @variables.inspect
1360
+ # end
1361
+ #
1362
+ # def inspect
1363
+ # to_s
1364
+ # end
1365
+ # end
1366
+ #
1367
+ # class TypeFactory
1368
+ # include TypeCheck
1369
+ #
1370
+ # def initialize(pre_types, variable_mapping)
1371
+ # @pre_types = is_kind_of! pre_types, Hash
1372
+ # @variable_mapping = is_kind_of! variable_mapping, Hash
1373
+ # @types = {}
1374
+ # end
1375
+ #
1376
+ # def define
1377
+ # define_types
1378
+ # define_fields_and_variants
1379
+ # eval_definitions
1380
+ #
1381
+ # @types
1382
+ # end
1383
+ #
1384
+ # private
1385
+ #
1386
+ # def define_types
1387
+ # @pre_types.each do |name, pre_type|
1388
+ # @types[name] = pre_type.kind.allocate
1389
+ # end
1390
+ # end
1391
+ #
1392
+ # def define_fields_and_variants
1393
+ # select = ->(klass, &block) do
1394
+ # @pre_types.select { |_, pre_type| pre_type.kind == klass }.each &block
1395
+ # end
1396
+ #
1397
+ # select.(Atom) do |name, pre_type|
1398
+ # @types[name].send :initialize, type_name(pre_type)
1399
+ # end
1400
+ #
1401
+ # select.(Product) do |name, pre_type|
1402
+ # @types[name].send :initialize, type_name(pre_type), *pre_type.fields.map { |f| get_class f }
1403
+ # end
1404
+ #
1405
+ # select.(Variant) do |name, pre_type|
1406
+ # @types[name].send :initialize, type_name(pre_type), *pre_type.variants.map { |v| get_class v }
1407
+ # end
1408
+ #
1409
+ # select.(ProductVariant) do |name, pre_type|
1410
+ # @types[name].send :initialize, type_name(pre_type),
1411
+ # pre_type.fields.map { |f| get_class f },
1412
+ # pre_type.variants.map { |v| get_class v }
1413
+ # end
1414
+ # end
1415
+ #
1416
+ # def eval_definitions
1417
+ # @pre_types.each do |name, pre_type|
1418
+ # next unless pre_type.definition
1419
+ # type = get_class name
1420
+ # type.module_eval &pre_type.definition
1421
+ # end
1422
+ # end
1423
+ #
1424
+ # def get_class(key)
1425
+ # if key.kind_of? Symbol
1426
+ # @variable_mapping[key] or raise "missing variable mapping for #{key}"
1427
+ # elsif key.kind_of? String
1428
+ # @types[key] or raise ArgumentError
1429
+ # elsif key.kind_of? PreType
1430
+ # @types[key.name]
1431
+ # elsif key.kind_of? Hash
1432
+ # key.each { |k, v| key[k] = get_class v }
1433
+ # else
1434
+ # key
1435
+ # end
1436
+ # end
1437
+ #
1438
+ # def type_name(pre_type)
1439
+ # pre_type.const_name + if pre_type.variables?
1440
+ # '[' + pre_type.variables.map { |v| @variable_mapping[v] } * ',' + ']'
1441
+ # else
1442
+ # ''
1443
+ # end
1444
+ # end
1445
+ # end
1446
+ #
1447
+ # class Environment
1448
+ # attr_reader :pre_types
1449
+ # def initialize(&definition)
1450
+ # @pre_types = {}
1451
+ # @types = {}
1452
+ # instance_eval &definition
1453
+ # end
1454
+ #
1455
+ # def method_missing(method, *fields, &definition)
1456
+ # name = method.to_s
1457
+ # @pre_types[name] ||= PreType.new(self, name)
1458
+ # @pre_types[name].fields = fields unless fields.empty?
1459
+ # @pre_types[name].definition = definition if definition
1460
+ # @pre_types[name]
1461
+ # end
1462
+ #
1463
+ # def run
1464
+ # pre_types = @pre_types.select { |_, pt| pt.no_variables? }
1465
+ # types = TypeFactory.new(pre_types, {}).define
1466
+ # constants = pre_types.inject({}) do |hash, (name, pre_type)|
1467
+ # hash.update pre_type.const_name => types[name]
1468
+ # end
1469
+ #
1470
+ # pre_constructors = @pre_types.select { |_, pt| pt.variables? }
1471
+ # constructors = pre_constructors.inject({}) do |hash, (name, pt)|
1472
+ # pre_types = Hash[([pt] + pt.dependent).map { |pt| [pt.name, pt] }]
1473
+ # hash.update name => TypeConstructor.new(pre_types, pt.const_name, pt.variables)
1474
+ # end
1475
+ #
1476
+ # constants.update Hash[constructors.map { |name, c| [c.const_name, c] }]
1477
+ #
1478
+ # @pre_types.map { |name, pre_type| types[name] || constructors[name] || raise("missing #{name}") }
1479
+ # end
1480
+ #
1481
+ # private
1482
+ #
1483
+ # #def define_constants(map)
1484
+ # # map.each { |const_name, value| @base.const_set const_name.to_sym, value } if @base
1485
+ # #end
1486
+ # end
1487
+ #
1488
+ # def type_def(&definition)
1489
+ # Environment.new(&definition).run
1490
+ # end
1491
+ #end
1492
+
1493
+ #extend DSL
1072
1494
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: algebrick
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Petr Chalupa