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.
- checksums.yaml +4 -4
- data/README.md +8 -7
- data/README_FULL.md +10 -11
- data/VERSION +1 -1
- data/lib/algebrick.rb +770 -348
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f70cafbfe1296efd6042757bf59917fb2f290aa
|
4
|
+
data.tar.gz: 3466daa431dd466059085c9e6acf42eb872a7335
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
19
|
+
Let's define a Tree
|
21
20
|
|
22
21
|
```ruby
|
23
|
-
|
22
|
+
Tree = Algebrick.type do |tree|
|
23
|
+
Empty = type
|
24
|
+
Leaf = type { fields Integer }
|
25
|
+
Node = type { fields tree, tree }
|
24
26
|
|
25
|
-
|
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
|
-
|
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
|
29
|
+
There are 4 kinds of algebraic types:
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
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)
|
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
|
+
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)
|
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
|
320
|
-
|
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
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
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
|
405
|
-
@constructor
|
386
|
+
def new(*fields)
|
387
|
+
raise TypeError unless @constructor
|
388
|
+
@constructor.new *fields
|
406
389
|
end
|
407
390
|
|
408
|
-
|
409
|
-
|
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
|
461
|
-
attr_reader :
|
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
|
487
|
-
|
488
|
-
|
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
|
516
|
-
|
537
|
+
def fields(*fields)
|
538
|
+
@new_type.set_fields fields
|
539
|
+
self
|
517
540
|
end
|
518
541
|
|
519
|
-
def
|
520
|
-
|
542
|
+
def variants(*variants)
|
543
|
+
@new_type.set_variants variants
|
544
|
+
self
|
521
545
|
end
|
522
546
|
|
523
|
-
def
|
524
|
-
|
547
|
+
def type(&block)
|
548
|
+
Algebrick.type &block
|
525
549
|
end
|
526
550
|
end
|
527
551
|
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
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
|
-
|
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
|
-
|
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::
|
862
|
-
|
863
|
-
field_matchers
|
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
|
-
|
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
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
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
|