algebrick 0.2.5 → 0.3.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/{LICENSE → LICENSE.txt} +0 -0
- data/README_FULL.md +4 -0
- data/VERSION +1 -1
- data/lib/algebrick.rb +169 -639
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c01f50d4cf102467631891110b1ee7df7d90375e
|
4
|
+
data.tar.gz: ff62352d8e0893a2bc6f4c2d71e183aa6bfbcdb2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b0ac037c8e10a26de02a2194085248f18947034713db0a9cb4d0b687052df3160f73e86ea3715c74f570cd41987e4f99b6bde87e630d96cc4be23f9edc1c9e3
|
7
|
+
data.tar.gz: 2ba7c6fb2338c87d9fb51a8e5890d321742e6878ae7e1b10cff9da409b5b4054484340d0f3a6c598f5c22be94fddf6da20e881bebffaf85da74ef0ffd8d72e10
|
data/{LICENSE → LICENSE.txt}
RENAMED
File without changes
|
data/README_FULL.md
CHANGED
@@ -54,6 +54,10 @@ Algebraic matchers are helper objects to match algebraic values and others with
|
|
54
54
|
|
55
55
|
{include:file:doc/pattern_matching.out.rb}
|
56
56
|
|
57
|
+
### Parametrized types
|
58
|
+
|
59
|
+
{include:file:doc/parametrized.out.rb}
|
60
|
+
|
57
61
|
## What is it good for?
|
58
62
|
|
59
63
|
### Defining data with a given structure
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/lib/algebrick.rb
CHANGED
@@ -14,24 +14,18 @@
|
|
14
14
|
|
15
15
|
|
16
16
|
# TODO method definition in variant type defines methods on variants based on match, better performance?
|
17
|
-
# TODO type variables/constructor maybe(a) === none | a
|
18
17
|
# TODO add matcher/s for Hash
|
19
18
|
# TODO add method matcher (:size, matcher)
|
20
19
|
# TODO Menu modeling example, add TypedArray
|
21
20
|
# TODO update actor pattern example when gem is done
|
22
|
-
# TODO example with birth-number Valid|Invalid
|
23
21
|
|
24
|
-
require '
|
22
|
+
require 'monitor'
|
25
23
|
|
26
|
-
#class Module
|
27
|
-
# # Return any modules we +extend+
|
28
|
-
# def extended_modules
|
29
|
-
# class << self
|
30
|
-
# self
|
31
|
-
# end.included_modules
|
32
|
-
# end
|
33
|
-
#end
|
34
24
|
|
25
|
+
# Provides Algebraic types and pattern matching
|
26
|
+
#
|
27
|
+
# **Quick example**
|
28
|
+
# {include:file:doc/quick_example.out.rb}
|
35
29
|
module Algebrick
|
36
30
|
|
37
31
|
def self.version
|
@@ -75,30 +69,13 @@ module Algebrick
|
|
75
69
|
end
|
76
70
|
end
|
77
71
|
|
72
|
+
# include this module anywhere yoy need to use pattern matching
|
78
73
|
module Matching
|
79
74
|
def any
|
80
75
|
Matchers::Any.new
|
81
76
|
end
|
82
77
|
|
83
|
-
alias_method :_, :any
|
84
|
-
|
85
|
-
#match Empty,
|
86
|
-
# Empty >> static_value_like_string,
|
87
|
-
# Leaf.(~any) >-> value do
|
88
|
-
# value
|
89
|
-
# end
|
90
|
-
#match Empty,
|
91
|
-
# Node => ->() {},
|
92
|
-
# Empty => ->() {},
|
93
|
-
# Leaf.(~any) => ->(value) { value }
|
94
|
-
#match Empty,
|
95
|
-
# [Node, lambda {}],
|
96
|
-
# [Empty, lambda {}],
|
97
|
-
# [Leaf.(~any), lambda { |value| value }]
|
98
|
-
#match(Empty,
|
99
|
-
# Node.case {},
|
100
|
-
# Empty.case {},
|
101
|
-
# Leaf.(~any).case { |value| value })
|
78
|
+
alias_method :_, :any
|
102
79
|
|
103
80
|
def match(value, *cases)
|
104
81
|
cases = if cases.size == 1 && cases.first.is_a?(Hash)
|
@@ -108,14 +85,14 @@ module Algebrick
|
|
108
85
|
end
|
109
86
|
|
110
87
|
cases.each do |matcher, block|
|
111
|
-
return match_value matcher, block if matcher === value
|
88
|
+
return Matching.match_value matcher, block if matcher === value
|
112
89
|
end
|
113
90
|
raise "no match for (#{value.class}) '#{value}' by any of #{cases.map(&:first).join ', '}"
|
114
91
|
end
|
115
92
|
|
116
93
|
private
|
117
94
|
|
118
|
-
def match_value(matcher, block)
|
95
|
+
def self.match_value(matcher, block)
|
119
96
|
if block.kind_of? Proc
|
120
97
|
if matcher.kind_of? Matchers::Abstract
|
121
98
|
matcher.assigns &block
|
@@ -152,10 +129,6 @@ module Algebrick
|
|
152
129
|
!to_m
|
153
130
|
end
|
154
131
|
|
155
|
-
def -(block)
|
156
|
-
to_m - block
|
157
|
-
end
|
158
|
-
|
159
132
|
def case(&block)
|
160
133
|
to_m.case &block
|
161
134
|
end
|
@@ -169,6 +142,7 @@ module Algebrick
|
|
169
142
|
end
|
170
143
|
end
|
171
144
|
|
145
|
+
# Any Algebraic type defined by Algebrick is kind of Type
|
172
146
|
class Type < Module
|
173
147
|
include TypeCheck
|
174
148
|
include Matching
|
@@ -204,6 +178,7 @@ module Algebrick
|
|
204
178
|
end
|
205
179
|
end
|
206
180
|
|
181
|
+
# Any value of Algebraic type is kind of Value
|
207
182
|
module Value
|
208
183
|
include TypeCheck
|
209
184
|
include Matching
|
@@ -232,6 +207,7 @@ module Algebrick
|
|
232
207
|
TYPE_KEY = :algebrick
|
233
208
|
FIELDS_KEY = :fields
|
234
209
|
|
210
|
+
# Representation of Atomic types
|
235
211
|
class Atom < Type
|
236
212
|
include Value
|
237
213
|
|
@@ -273,6 +249,7 @@ module Algebrick
|
|
273
249
|
end
|
274
250
|
end
|
275
251
|
|
252
|
+
# A private class used for Product values creation
|
276
253
|
class ProductConstructor
|
277
254
|
include Value
|
278
255
|
attr_reader :fields
|
@@ -338,15 +315,25 @@ module Algebrick
|
|
338
315
|
end
|
339
316
|
end
|
340
317
|
|
318
|
+
# Representation of Product and Variant types. The class behaves differently
|
319
|
+
# based on #kind.
|
341
320
|
class ProductVariant < Type
|
342
321
|
attr_reader :fields, :variants
|
343
322
|
|
323
|
+
def initialize(name, &definition)
|
324
|
+
super(name, &definition)
|
325
|
+
@to_be_kind_of = []
|
326
|
+
end
|
327
|
+
|
344
328
|
def set_fields(fields_or_hash)
|
345
329
|
raise TypeError, 'can be set only once' if @fields
|
346
|
-
fields, keys =
|
347
|
-
|
348
|
-
|
330
|
+
fields, keys = case fields_or_hash
|
331
|
+
when Hash
|
332
|
+
[fields_or_hash.values, fields_or_hash.keys]
|
333
|
+
when Array
|
349
334
|
[fields_or_hash, nil]
|
335
|
+
else
|
336
|
+
raise ArgumentError
|
350
337
|
end
|
351
338
|
|
352
339
|
set_field_names keys if keys
|
@@ -356,6 +343,8 @@ module Algebrick
|
|
356
343
|
define_method(:value) { @fields.first } if fields.size == 1
|
357
344
|
@fields = fields
|
358
345
|
@constructor = Class.new(ProductConstructor).tap { |c| c.type = self }
|
346
|
+
apply_be_kind_of
|
347
|
+
self
|
359
348
|
end
|
360
349
|
|
361
350
|
def field_names
|
@@ -387,27 +376,11 @@ module Algebrick
|
|
387
376
|
add_field_method_readers *@field_names
|
388
377
|
end
|
389
378
|
|
390
|
-
raise 'remove deprecation' if Algebrick.version >= Gem::Version.new('0.3')
|
391
|
-
|
392
|
-
def add_all_field_method_accessors
|
393
|
-
warn "add_all_field_method_accessors is deprecated, it'll be removed in 0.3\n#{caller[0]}"
|
394
|
-
add_all_field_method_readers
|
395
|
-
end
|
396
|
-
|
397
|
-
def add_field_method_accessors(*fields)
|
398
|
-
warn "add_all_field_method_accessors is deprecated, it'll be removed in 0.3\n#{caller[0]}"
|
399
|
-
add_field_method_readers *fields
|
400
|
-
end
|
401
|
-
|
402
|
-
def add_field_method_accessor(field)
|
403
|
-
warn "add_all_field_method_accessors is deprecated, it'll be removed in 0.3\n#{caller[0]}"
|
404
|
-
add_field_method_reader field
|
405
|
-
end
|
406
|
-
|
407
379
|
def set_variants(variants)
|
408
380
|
raise TypeError, 'can be set only once' if @variants
|
409
381
|
variants.all? { |v| is_kind_of! v, Type, Class }
|
410
382
|
@variants = variants
|
383
|
+
apply_be_kind_of
|
411
384
|
variants.each do |v|
|
412
385
|
if v.respond_to? :be_kind_of
|
413
386
|
v.be_kind_of self
|
@@ -415,10 +388,11 @@ module Algebrick
|
|
415
388
|
v.send :include, self
|
416
389
|
end
|
417
390
|
end
|
391
|
+
self
|
418
392
|
end
|
419
393
|
|
420
394
|
def new(*fields)
|
421
|
-
raise TypeError unless @constructor
|
395
|
+
raise TypeError, "#{self} does not have fields" unless @constructor
|
422
396
|
@constructor.new *fields
|
423
397
|
end
|
424
398
|
|
@@ -430,9 +404,15 @@ module Algebrick
|
|
430
404
|
end
|
431
405
|
|
432
406
|
def be_kind_of(type)
|
433
|
-
|
434
|
-
|
435
|
-
|
407
|
+
@to_be_kind_of << type
|
408
|
+
apply_be_kind_of
|
409
|
+
end
|
410
|
+
|
411
|
+
def apply_be_kind_of
|
412
|
+
@to_be_kind_of.each do |type|
|
413
|
+
@constructor.send :include, type if @constructor
|
414
|
+
variants.each { |v| v.be_kind_of type unless v == self } if @variants
|
415
|
+
end
|
436
416
|
end
|
437
417
|
|
438
418
|
def call(*field_matchers)
|
@@ -500,6 +480,15 @@ module Algebrick
|
|
500
480
|
end
|
501
481
|
end
|
502
482
|
|
483
|
+
def assigned_types
|
484
|
+
@assigned_types or raise TypeError, "#{self} does not have assigned types"
|
485
|
+
end
|
486
|
+
|
487
|
+
def assigned_types=(assigned_types)
|
488
|
+
raise TypeError, "#{self} assigned types already set" if @assigned_types
|
489
|
+
@assigned_types = assigned_types
|
490
|
+
end
|
491
|
+
|
503
492
|
private
|
504
493
|
|
505
494
|
def product_to_s
|
@@ -559,10 +548,108 @@ module Algebrick
|
|
559
548
|
end
|
560
549
|
end
|
561
550
|
|
551
|
+
class ParametrizedType < Module
|
552
|
+
include TypeCheck
|
553
|
+
include MatcherDelegations
|
554
|
+
|
555
|
+
attr_reader :variables, :fields, :variants
|
556
|
+
|
557
|
+
def initialize(variables)
|
558
|
+
@variables = variables.each { |v| is_kind_of! v, Symbol }
|
559
|
+
@fields = nil
|
560
|
+
@variants = nil
|
561
|
+
@cache = {}
|
562
|
+
@cache_barrier = Monitor.new
|
563
|
+
end
|
564
|
+
|
565
|
+
def set_fields(fields)
|
566
|
+
@fields = is_kind_of! fields, Hash, Array
|
567
|
+
end
|
568
|
+
|
569
|
+
def field_names
|
570
|
+
case @fields
|
571
|
+
when Hash
|
572
|
+
@fields.keys
|
573
|
+
when Array, nil
|
574
|
+
raise TypeError, "field names not defined on #{self}"
|
575
|
+
else
|
576
|
+
raise
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
580
|
+
def set_variants(variants)
|
581
|
+
@variants = is_kind_of! variants, Array
|
582
|
+
end
|
583
|
+
|
584
|
+
def [](*assigned_types)
|
585
|
+
@cache_barrier.synchronize do
|
586
|
+
@cache[assigned_types] || begin
|
587
|
+
raise ArgumentError unless assigned_types.size == variables.size
|
588
|
+
ProductVariant.new(type_name(assigned_types)).tap do |type|
|
589
|
+
type.be_kind_of self
|
590
|
+
@cache[assigned_types] = type
|
591
|
+
type.assigned_types = assigned_types
|
592
|
+
type.set_variants insert_types(variants, assigned_types) if variants
|
593
|
+
type.set_fields insert_types(fields, assigned_types) if fields
|
594
|
+
end
|
595
|
+
end
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
def to_s
|
600
|
+
"#{name}[#{variables.join(', ')}]"
|
601
|
+
end
|
602
|
+
|
603
|
+
def inspect
|
604
|
+
to_s
|
605
|
+
end
|
606
|
+
|
607
|
+
def to_m
|
608
|
+
if @variants
|
609
|
+
Matchers::Variant.new self
|
610
|
+
else
|
611
|
+
Matchers::Product.new self
|
612
|
+
end
|
613
|
+
end
|
614
|
+
|
615
|
+
def call(*field_matchers)
|
616
|
+
raise TypeError unless @fields
|
617
|
+
Matchers::Product.new self, *field_matchers
|
618
|
+
end
|
619
|
+
|
620
|
+
private
|
621
|
+
|
622
|
+
def insert_types(types, assigned_types)
|
623
|
+
case types
|
624
|
+
when Hash
|
625
|
+
types.inject({}) { |h, (k, v)| h.update k => insert_type(v, assigned_types) }
|
626
|
+
when Array
|
627
|
+
types.map { |v| insert_type v, assigned_types }
|
628
|
+
else
|
629
|
+
raise ArgumentError
|
630
|
+
end
|
631
|
+
end
|
632
|
+
|
633
|
+
def insert_type(type, assigned_types)
|
634
|
+
case type
|
635
|
+
when Symbol
|
636
|
+
assigned_types[variables.index type]
|
637
|
+
when ParametrizedType
|
638
|
+
type[*type.variables.map { |v| assigned_types[variables.index v] }]
|
639
|
+
else
|
640
|
+
type
|
641
|
+
end
|
642
|
+
end
|
643
|
+
|
644
|
+
def type_name(assigned_types)
|
645
|
+
"#{name}[#{assigned_types.join(', ')}]"
|
646
|
+
end
|
647
|
+
end
|
648
|
+
|
562
649
|
module DSL
|
563
650
|
module Shortcuts
|
564
|
-
def type(&block)
|
565
|
-
Algebrick.type &block
|
651
|
+
def type(*variables, &block)
|
652
|
+
Algebrick.type *variables, &block
|
566
653
|
end
|
567
654
|
|
568
655
|
def atom
|
@@ -572,17 +659,18 @@ module Algebrick
|
|
572
659
|
|
573
660
|
class TypeDefinitionScope
|
574
661
|
include Shortcuts
|
662
|
+
include TypeCheck
|
575
663
|
|
576
664
|
attr_reader :new_type
|
577
665
|
|
578
|
-
def initialize(&block)
|
579
|
-
@new_type = ProductVariant
|
666
|
+
def initialize(new_type, &block)
|
667
|
+
@new_type = is_kind_of! new_type, ProductVariant, ParametrizedType
|
580
668
|
instance_exec @new_type, &block
|
581
|
-
@new_type.kind
|
669
|
+
@new_type.kind if @new_type.is_a? ProductVariant
|
582
670
|
end
|
583
671
|
|
584
672
|
def fields(*fields)
|
585
|
-
@new_type.set_fields fields
|
673
|
+
@new_type.set_fields fields.first.is_a?(Hash) ? fields.first : fields
|
586
674
|
self
|
587
675
|
end
|
588
676
|
|
@@ -620,11 +708,16 @@ module Algebrick
|
|
620
708
|
end
|
621
709
|
end
|
622
710
|
|
623
|
-
def self.type(&block)
|
711
|
+
def self.type(*variables, &block)
|
624
712
|
if block.nil?
|
713
|
+
raise 'Atom canot be parametrized' unless variables.empty?
|
625
714
|
atom
|
626
715
|
else
|
627
|
-
|
716
|
+
if variables.empty?
|
717
|
+
DSL::TypeDefinitionScope.new(ProductVariant.new(nil), &block).new_type
|
718
|
+
else
|
719
|
+
DSL::TypeDefinitionScope.new(ParametrizedType.new(variables), &block).new_type
|
720
|
+
end
|
628
721
|
end
|
629
722
|
end
|
630
723
|
|
@@ -650,19 +743,11 @@ module Algebrick
|
|
650
743
|
return self, block
|
651
744
|
end
|
652
745
|
|
653
|
-
raise 'remove deprecation' if Algebrick.version >= Gem::Version.new('0.3')
|
654
|
-
|
655
|
-
def -(block)
|
656
|
-
warn "a 'matcher --> {}' and 'matcher +-> {}' is deprecated, it'll be removed in 0.3\n#{caller[0]}"
|
657
|
-
self > block
|
658
|
-
end
|
659
|
-
|
660
746
|
def >(block)
|
661
747
|
return self, block
|
662
748
|
end
|
663
749
|
|
664
750
|
alias_method :>>, :>
|
665
|
-
alias_method :+, :-
|
666
751
|
|
667
752
|
def ~
|
668
753
|
@assign = true
|
@@ -954,12 +1039,15 @@ module Algebrick
|
|
954
1039
|
|
955
1040
|
def initialize(algebraic_type, *field_matchers)
|
956
1041
|
super()
|
957
|
-
@algebraic_type = is_kind_of! algebraic_type, Algebrick::ProductVariant
|
1042
|
+
@algebraic_type = is_kind_of! algebraic_type, Algebrick::ProductVariant, Algebrick::ParametrizedType
|
958
1043
|
raise ArgumentError unless algebraic_type.fields
|
959
1044
|
@field_matchers = case
|
1045
|
+
|
1046
|
+
# AProduct.()
|
960
1047
|
when field_matchers.empty?
|
961
1048
|
::Array.new(algebraic_type.fields.size) { Algebrick.any }
|
962
1049
|
|
1050
|
+
# AProduct.(field_name: a_matcher)
|
963
1051
|
when field_matchers.size == 1 && field_matchers.first.is_a?(Hash)
|
964
1052
|
field_matchers = field_matchers.first
|
965
1053
|
unless (dif = field_matchers.keys - algebraic_type.field_names).empty?
|
@@ -968,7 +1056,9 @@ module Algebrick
|
|
968
1056
|
algebraic_type.field_names.map do |field|
|
969
1057
|
field_matchers[field] || Algebrick.any
|
970
1058
|
end
|
971
|
-
|
1059
|
+
|
1060
|
+
# AProduct.(:field_name)
|
1061
|
+
when field_matchers.all? { |v| v.is_a? Symbol }
|
972
1062
|
unless (dif = field_matchers - algebraic_type.field_names).empty?
|
973
1063
|
raise ArgumentError, "no #{dif} fields in #{algebraic_type}"
|
974
1064
|
end
|
@@ -976,6 +1066,7 @@ module Algebrick
|
|
976
1066
|
field_matchers.include?(field) ? ~Algebrick.any : Algebrick.any
|
977
1067
|
end
|
978
1068
|
|
1069
|
+
# normal
|
979
1070
|
else
|
980
1071
|
field_matchers
|
981
1072
|
end
|
@@ -1032,565 +1123,4 @@ module Algebrick
|
|
1032
1123
|
end
|
1033
1124
|
end
|
1034
1125
|
|
1035
|
-
#class AbstractProductVariant < Type
|
1036
|
-
# def be_kind_of(type = nil)
|
1037
|
-
# if initialized?
|
1038
|
-
# be_kind_of! type if type
|
1039
|
-
# if @to_be_kind_of
|
1040
|
-
# while (type = @to_be_kind_of.shift)
|
1041
|
-
# be_kind_of! type
|
1042
|
-
# end
|
1043
|
-
# end
|
1044
|
-
# else
|
1045
|
-
# @to_be_kind_of ||= []
|
1046
|
-
# @to_be_kind_of << type if type
|
1047
|
-
# end
|
1048
|
-
# self
|
1049
|
-
# end
|
1050
|
-
#
|
1051
|
-
# def add_field_method_accessor(field)
|
1052
|
-
# raise TypeError, 'no field names' unless @field_names
|
1053
|
-
# raise TypeError, "no field name #{field}" unless @field_names.include? field
|
1054
|
-
# define_method(field) { self[field] }
|
1055
|
-
# self
|
1056
|
-
# end
|
1057
|
-
#
|
1058
|
-
# def add_field_method_accessors(*fields)
|
1059
|
-
# fields.each { |f| add_field_method_accessor f }
|
1060
|
-
# self
|
1061
|
-
# end
|
1062
|
-
#
|
1063
|
-
# def add_all_field_method_accessors
|
1064
|
-
# add_field_method_accessors *@field_names
|
1065
|
-
# end
|
1066
|
-
#
|
1067
|
-
# protected
|
1068
|
-
#
|
1069
|
-
# def be_kind_of!(type)
|
1070
|
-
# raise NotImplementedError
|
1071
|
-
# end
|
1072
|
-
#
|
1073
|
-
# private
|
1074
|
-
#
|
1075
|
-
# def initialized?
|
1076
|
-
# !!@initialized
|
1077
|
-
# end
|
1078
|
-
#
|
1079
|
-
# def initialize(name, &block)
|
1080
|
-
# super name, &block
|
1081
|
-
# @initialized = true
|
1082
|
-
# be_kind_of
|
1083
|
-
# end
|
1084
|
-
#
|
1085
|
-
# def set_fields(fields_or_hash)
|
1086
|
-
# fields, keys = if fields_or_hash.size == 1 && fields_or_hash.first.is_a?(Hash)
|
1087
|
-
# [fields_or_hash.first.values, fields_or_hash.first.keys]
|
1088
|
-
# else
|
1089
|
-
# [fields_or_hash, nil]
|
1090
|
-
# end
|
1091
|
-
#
|
1092
|
-
# set_field_names keys if keys
|
1093
|
-
#
|
1094
|
-
# fields.all? { |f| is_kind_of! f, Type, Class }
|
1095
|
-
# raise TypeError, 'there is no product with zero fields' unless fields.size > 0
|
1096
|
-
# define_method(:value) { @fields.first } if fields.size == 1
|
1097
|
-
# @fields = fields
|
1098
|
-
# @constructor = Class.new(ProductConstructor).tap { |c| c.type = self }
|
1099
|
-
# end
|
1100
|
-
#
|
1101
|
-
# def set_field_names(names)
|
1102
|
-
# @field_names = names
|
1103
|
-
# names.all? { |k| is_kind_of! k, Symbol }
|
1104
|
-
# dict = @field_indexes =
|
1105
|
-
# Hash.new { |h, k| raise ArgumentError, "unknown field #{k.inspect} in #{self}" }.
|
1106
|
-
# update names.each_with_index.inject({}) { |h, (k, i)| h.update k => i }
|
1107
|
-
# define_method(:[]) { |key| @fields[dict[key]] }
|
1108
|
-
# end
|
1109
|
-
#
|
1110
|
-
# def set_variants(variants)
|
1111
|
-
# variants.all? { |v| is_kind_of! v, Type, Class }
|
1112
|
-
# @variants = variants
|
1113
|
-
# variants.each do |v|
|
1114
|
-
# if v.respond_to? :be_kind_of
|
1115
|
-
# v.be_kind_of self
|
1116
|
-
# else
|
1117
|
-
# v.send :include, self
|
1118
|
-
# end
|
1119
|
-
# end
|
1120
|
-
# end
|
1121
|
-
#
|
1122
|
-
# def product_be_kind_of(type)
|
1123
|
-
# @constructor.send :include, type
|
1124
|
-
# end
|
1125
|
-
#
|
1126
|
-
# def construct_product(*fields)
|
1127
|
-
# @constructor.new *fields
|
1128
|
-
# end
|
1129
|
-
#
|
1130
|
-
# def product_to_s
|
1131
|
-
# fields_str = if field_names
|
1132
|
-
# field_names.zip(fields).map { |name, field| "#{name}: #{field.name}" }
|
1133
|
-
# else
|
1134
|
-
# fields.map(&:name)
|
1135
|
-
# end
|
1136
|
-
# "#{name}(#{fields_str.join ', '})"
|
1137
|
-
# end
|
1138
|
-
#
|
1139
|
-
# def product_from_hash(hash)
|
1140
|
-
# (type_name = hash[TYPE_KEY] || hash[TYPE_KEY.to_s]) or
|
1141
|
-
# raise ArgumentError, "hash does not have #{TYPE_KEY}"
|
1142
|
-
# raise ArgumentError, "#{type_name} is not #{name}" unless type_name == name
|
1143
|
-
#
|
1144
|
-
# fields = hash[FIELDS_KEY] || hash[FIELDS_KEY.to_s] ||
|
1145
|
-
# hash.reject { |k, _| k.to_s == TYPE_KEY.to_s }
|
1146
|
-
# is_kind_of! fields, Hash, Array
|
1147
|
-
#
|
1148
|
-
# case fields
|
1149
|
-
# when Array
|
1150
|
-
# self[*fields.map { |value| field_from_hash value }]
|
1151
|
-
# when Hash
|
1152
|
-
# self[fields.inject({}) do |h, (name, value)|
|
1153
|
-
# raise ArgumentError unless @field_names.map(&:to_s).include? name.to_s
|
1154
|
-
# h.update name.to_sym => field_from_hash(value)
|
1155
|
-
# end]
|
1156
|
-
# end
|
1157
|
-
# end
|
1158
|
-
#
|
1159
|
-
# def field_from_hash(hash)
|
1160
|
-
# return hash unless Hash === hash
|
1161
|
-
# (type_name = hash[TYPE_KEY] || hash[TYPE_KEY.to_s]) or return hash
|
1162
|
-
# type = constantize type_name
|
1163
|
-
# type.from_hash hash
|
1164
|
-
# end
|
1165
|
-
#
|
1166
|
-
# def constantize(camel_cased_word)
|
1167
|
-
# names = camel_cased_word.split('::')
|
1168
|
-
# names.shift if names.empty? || names.first.empty?
|
1169
|
-
#
|
1170
|
-
# constant = Object
|
1171
|
-
# names.each do |name|
|
1172
|
-
# constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
1173
|
-
# end
|
1174
|
-
# constant
|
1175
|
-
# end
|
1176
|
-
#end
|
1177
|
-
#
|
1178
|
-
#class Product < AbstractProductVariant
|
1179
|
-
# attr_reader :fields, :field_names, :field_indexes
|
1180
|
-
#
|
1181
|
-
# def initialize(name, *fields, &block)
|
1182
|
-
# set_fields fields
|
1183
|
-
# super(name, &block)
|
1184
|
-
# end
|
1185
|
-
#
|
1186
|
-
# def new(*fields)
|
1187
|
-
# construct_product(*fields)
|
1188
|
-
# end
|
1189
|
-
#
|
1190
|
-
# alias_method :[], :new
|
1191
|
-
#
|
1192
|
-
# def be_kind_of!(type)
|
1193
|
-
# product_be_kind_of type
|
1194
|
-
# end
|
1195
|
-
#
|
1196
|
-
# def call(*field_matchers)
|
1197
|
-
# Matchers::Product.new self, *field_matchers
|
1198
|
-
# end
|
1199
|
-
#
|
1200
|
-
# def to_m
|
1201
|
-
# call *::Array.new(fields.size) { Algebrick.any }
|
1202
|
-
# end
|
1203
|
-
#
|
1204
|
-
# def ==(other)
|
1205
|
-
# other.kind_of? Product and fields == other.fields
|
1206
|
-
# end
|
1207
|
-
#
|
1208
|
-
# def to_s
|
1209
|
-
# product_to_s
|
1210
|
-
# end
|
1211
|
-
#
|
1212
|
-
# def from_hash(hash)
|
1213
|
-
# product_from_hash hash
|
1214
|
-
# end
|
1215
|
-
#end
|
1216
|
-
#
|
1217
|
-
#class Variant < AbstractProductVariant
|
1218
|
-
# attr_reader :variants
|
1219
|
-
#
|
1220
|
-
# def initialize(name, *variants, &block)
|
1221
|
-
# set_variants(variants)
|
1222
|
-
# super name, &block
|
1223
|
-
# end
|
1224
|
-
#
|
1225
|
-
# def be_kind_of!(type)
|
1226
|
-
# variants.each { |v| v.be_kind_of type }
|
1227
|
-
# end
|
1228
|
-
#
|
1229
|
-
# def to_m
|
1230
|
-
# Matchers::Variant.new self
|
1231
|
-
# end
|
1232
|
-
#
|
1233
|
-
# def ==(other)
|
1234
|
-
# other.kind_of? Variant and variants == other.variants
|
1235
|
-
# end
|
1236
|
-
#
|
1237
|
-
# def to_s
|
1238
|
-
# "#{name}(#{variants.map(&:name).join ' | '})"
|
1239
|
-
# end
|
1240
|
-
#
|
1241
|
-
# def from_hash(hash)
|
1242
|
-
# field_from_hash hash
|
1243
|
-
# end
|
1244
|
-
#end
|
1245
|
-
#
|
1246
|
-
#class ProductVariant < AbstractProductVariant
|
1247
|
-
# attr_reader :fields, :field_names, :field_indexes, :variants
|
1248
|
-
#
|
1249
|
-
# def initialize(name, fields, variants, &block)
|
1250
|
-
# set_fields fields
|
1251
|
-
# raise unless variants.include? self
|
1252
|
-
# set_variants variants
|
1253
|
-
# super name, &block
|
1254
|
-
# end
|
1255
|
-
#
|
1256
|
-
# def be_kind_of!(type)
|
1257
|
-
# variants.each { |v| v.be_kind_of type unless v == self }
|
1258
|
-
# product_be_kind_of type
|
1259
|
-
# end
|
1260
|
-
#
|
1261
|
-
# def call(*field_matchers)
|
1262
|
-
# Matchers::Product.new self, *field_matchers
|
1263
|
-
# end
|
1264
|
-
#
|
1265
|
-
# def to_m
|
1266
|
-
# Matchers::Variant.new self
|
1267
|
-
# end
|
1268
|
-
#
|
1269
|
-
# def new(*fields)
|
1270
|
-
# construct_product(*fields)
|
1271
|
-
# end
|
1272
|
-
#
|
1273
|
-
# alias_method :[], :new
|
1274
|
-
#
|
1275
|
-
# def ==(other)
|
1276
|
-
# other.kind_of? ProductVariant and
|
1277
|
-
# variants == other.variants and fields == other.fields
|
1278
|
-
# end
|
1279
|
-
#
|
1280
|
-
# def to_s
|
1281
|
-
# name + '(' +
|
1282
|
-
# variants.map do |variant|
|
1283
|
-
# if variant == self
|
1284
|
-
# product_to_s
|
1285
|
-
# else
|
1286
|
-
# variant.name
|
1287
|
-
# end
|
1288
|
-
# end.join(' | ') +
|
1289
|
-
# ')'
|
1290
|
-
# end
|
1291
|
-
#
|
1292
|
-
# def from_hash(hash)
|
1293
|
-
# product_from_hash hash
|
1294
|
-
# end
|
1295
|
-
#end
|
1296
|
-
|
1297
|
-
#module DSL
|
1298
|
-
# -> do
|
1299
|
-
# maybe[:a] === none | some(:a)
|
1300
|
-
# tree[:a] === tip | tree(:a, tree, tree)
|
1301
|
-
# end
|
1302
|
-
#
|
1303
|
-
# -> do
|
1304
|
-
# maybe[:a].can_be none,
|
1305
|
-
# some.has(:a)
|
1306
|
-
#
|
1307
|
-
# maybe[:a].can_be none,
|
1308
|
-
# some.having(:a)
|
1309
|
-
#
|
1310
|
-
# maybe[:a].can_be none,
|
1311
|
-
# some.having(:a)
|
1312
|
-
# tree.can_be tip,
|
1313
|
-
# tree.having(Object, tree, tree)
|
1314
|
-
#
|
1315
|
-
# tree.can_be empty,
|
1316
|
-
# leaf.having(Object),
|
1317
|
-
# node.having(left: tree, right: tree)
|
1318
|
-
#
|
1319
|
-
# tree do
|
1320
|
-
# # def ...
|
1321
|
-
# end
|
1322
|
-
# end
|
1323
|
-
#
|
1324
|
-
# class PreType
|
1325
|
-
# include TypeCheck
|
1326
|
-
#
|
1327
|
-
# attr_reader :environment, :name, :fields, :variants, :definition, :variables
|
1328
|
-
#
|
1329
|
-
# def initialize(environment, name)
|
1330
|
-
# @environment = is_kind_of! environment, Environment
|
1331
|
-
# @name = is_kind_of! name, String
|
1332
|
-
# @fields = []
|
1333
|
-
# @variables = []
|
1334
|
-
# @variants = []
|
1335
|
-
# @definition = nil
|
1336
|
-
# end
|
1337
|
-
#
|
1338
|
-
# def const_name
|
1339
|
-
# @const_name ||= name.to_s.split('_').map { |s| s.tap { s[0] = s[0].upcase } }.join
|
1340
|
-
# end
|
1341
|
-
#
|
1342
|
-
# #def |(other)
|
1343
|
-
# # [self, other]
|
1344
|
-
# #end
|
1345
|
-
# #
|
1346
|
-
# #def to_ary
|
1347
|
-
# # [self]
|
1348
|
-
# #end
|
1349
|
-
#
|
1350
|
-
# def [](*variables)
|
1351
|
-
# variables.all? { |var| is_kind_of! var, Symbol }
|
1352
|
-
# @variables += variables
|
1353
|
-
# self
|
1354
|
-
# end
|
1355
|
-
#
|
1356
|
-
# def fields=(fields)
|
1357
|
-
# raise 'fields can be defined only once' unless @fields.empty?
|
1358
|
-
# fields.each do |field|
|
1359
|
-
# if Hash === field
|
1360
|
-
# field.each do |k, v|
|
1361
|
-
# is_kind_of! k, Symbol
|
1362
|
-
# is_kind_of! v, PreType, Type, Class, Symbol
|
1363
|
-
# @variables.push v if v.is_a? Symbol
|
1364
|
-
# end
|
1365
|
-
# else
|
1366
|
-
# is_kind_of! field, PreType, Type, Class, Symbol
|
1367
|
-
# @variables += fields.select { |v| v.is_a? Symbol }
|
1368
|
-
# end
|
1369
|
-
# end
|
1370
|
-
# @fields += fields
|
1371
|
-
# end
|
1372
|
-
#
|
1373
|
-
# def having(fields)
|
1374
|
-
# self.fields = fields
|
1375
|
-
# self
|
1376
|
-
# end
|
1377
|
-
#
|
1378
|
-
# alias_method :has, :having
|
1379
|
-
#
|
1380
|
-
# def fields_array
|
1381
|
-
# if (hash = @fields.find { |f| Hash === f })
|
1382
|
-
# hash.values
|
1383
|
-
# else
|
1384
|
-
# @fields
|
1385
|
-
# end
|
1386
|
-
# end
|
1387
|
-
#
|
1388
|
-
# #def dependent(set = Set.new)
|
1389
|
-
# # children = @variants + @fields
|
1390
|
-
# # children.each do |a_type|
|
1391
|
-
# # next if set.include? a_type
|
1392
|
-
# # next unless a_type.respond_to? :dependent
|
1393
|
-
# # set.add a_type
|
1394
|
-
# # a_type.dependent set
|
1395
|
-
# # end
|
1396
|
-
# # set.to_a
|
1397
|
-
# #end
|
1398
|
-
#
|
1399
|
-
# def definition=(block)
|
1400
|
-
# raise 'definition can be defined only once' if @definition
|
1401
|
-
# @definition = block
|
1402
|
-
# end
|
1403
|
-
#
|
1404
|
-
# def can_be(*variants)
|
1405
|
-
# raise 'variants can be defined only once' unless @variants.empty?
|
1406
|
-
# @variants = variants
|
1407
|
-
# self
|
1408
|
-
# end
|
1409
|
-
#
|
1410
|
-
# def no_variables?
|
1411
|
-
# !variables? and fields_array.select { |f| Symbol === f }.empty?
|
1412
|
-
# end
|
1413
|
-
#
|
1414
|
-
# def variables?
|
1415
|
-
# not @variables.empty?
|
1416
|
-
# end
|
1417
|
-
#
|
1418
|
-
# def kind
|
1419
|
-
# unless @variants.empty?
|
1420
|
-
# if @fields.empty?
|
1421
|
-
# Variant
|
1422
|
-
# else
|
1423
|
-
# ProductVariant
|
1424
|
-
# end
|
1425
|
-
# else
|
1426
|
-
# if @fields.empty?
|
1427
|
-
# Atom
|
1428
|
-
# else
|
1429
|
-
# Product
|
1430
|
-
# end
|
1431
|
-
# end
|
1432
|
-
# end
|
1433
|
-
# end
|
1434
|
-
#
|
1435
|
-
# class TypeConstructor
|
1436
|
-
# include TypeCheck
|
1437
|
-
# attr_reader :const_name
|
1438
|
-
#
|
1439
|
-
# def initialize(pre_types, const_name, variables)
|
1440
|
-
# warn "types with variables are experimental\n#{caller[0]}"
|
1441
|
-
# @pre_types = is_kind_of! pre_types, Hash
|
1442
|
-
# @const_name = is_kind_of! const_name, String
|
1443
|
-
# @variables = is_kind_of! variables, Array
|
1444
|
-
# @cache = {}
|
1445
|
-
# end
|
1446
|
-
#
|
1447
|
-
# def [](*variables)
|
1448
|
-
# variables.size == @variables.size or
|
1449
|
-
# raise ArgumentError, "variables size differs from #{@variables}"
|
1450
|
-
# @cache[variables] ||= begin
|
1451
|
-
#
|
1452
|
-
# TypeFactory.new(@pre_types, Hash[@variables.zip(variables)]).define.tap do |types|
|
1453
|
-
# types.each do |name, type|
|
1454
|
-
#
|
1455
|
-
# end
|
1456
|
-
# end
|
1457
|
-
# end
|
1458
|
-
# end
|
1459
|
-
#
|
1460
|
-
# def to_s
|
1461
|
-
# const_name + @variables.inspect
|
1462
|
-
# end
|
1463
|
-
#
|
1464
|
-
# def inspect
|
1465
|
-
# to_s
|
1466
|
-
# end
|
1467
|
-
# end
|
1468
|
-
#
|
1469
|
-
# class TypeFactory
|
1470
|
-
# include TypeCheck
|
1471
|
-
#
|
1472
|
-
# def initialize(pre_types, variable_mapping)
|
1473
|
-
# @pre_types = is_kind_of! pre_types, Hash
|
1474
|
-
# @variable_mapping = is_kind_of! variable_mapping, Hash
|
1475
|
-
# @types = {}
|
1476
|
-
# end
|
1477
|
-
#
|
1478
|
-
# def define
|
1479
|
-
# define_types
|
1480
|
-
# define_fields_and_variants
|
1481
|
-
# eval_definitions
|
1482
|
-
#
|
1483
|
-
# @types
|
1484
|
-
# end
|
1485
|
-
#
|
1486
|
-
# private
|
1487
|
-
#
|
1488
|
-
# def define_types
|
1489
|
-
# @pre_types.each do |name, pre_type|
|
1490
|
-
# @types[name] = pre_type.kind.allocate
|
1491
|
-
# end
|
1492
|
-
# end
|
1493
|
-
#
|
1494
|
-
# def define_fields_and_variants
|
1495
|
-
# select = ->(klass, &block) do
|
1496
|
-
# @pre_types.select { |_, pre_type| pre_type.kind == klass }.each &block
|
1497
|
-
# end
|
1498
|
-
#
|
1499
|
-
# select.(Atom) do |name, pre_type|
|
1500
|
-
# @types[name].send :initialize, type_name(pre_type)
|
1501
|
-
# end
|
1502
|
-
#
|
1503
|
-
# select.(Product) do |name, pre_type|
|
1504
|
-
# @types[name].send :initialize, type_name(pre_type), *pre_type.fields.map { |f| get_class f }
|
1505
|
-
# end
|
1506
|
-
#
|
1507
|
-
# select.(Variant) do |name, pre_type|
|
1508
|
-
# @types[name].send :initialize, type_name(pre_type), *pre_type.variants.map { |v| get_class v }
|
1509
|
-
# end
|
1510
|
-
#
|
1511
|
-
# select.(ProductVariant) do |name, pre_type|
|
1512
|
-
# @types[name].send :initialize, type_name(pre_type),
|
1513
|
-
# pre_type.fields.map { |f| get_class f },
|
1514
|
-
# pre_type.variants.map { |v| get_class v }
|
1515
|
-
# end
|
1516
|
-
# end
|
1517
|
-
#
|
1518
|
-
# def eval_definitions
|
1519
|
-
# @pre_types.each do |name, pre_type|
|
1520
|
-
# next unless pre_type.definition
|
1521
|
-
# type = get_class name
|
1522
|
-
# type.module_eval &pre_type.definition
|
1523
|
-
# end
|
1524
|
-
# end
|
1525
|
-
#
|
1526
|
-
# def get_class(key)
|
1527
|
-
# if key.kind_of? Symbol
|
1528
|
-
# @variable_mapping[key] or raise "missing variable mapping for #{key}"
|
1529
|
-
# elsif key.kind_of? String
|
1530
|
-
# @types[key] or raise ArgumentError
|
1531
|
-
# elsif key.kind_of? PreType
|
1532
|
-
# @types[key.name]
|
1533
|
-
# elsif key.kind_of? Hash
|
1534
|
-
# key.each { |k, v| key[k] = get_class v }
|
1535
|
-
# else
|
1536
|
-
# key
|
1537
|
-
# end
|
1538
|
-
# end
|
1539
|
-
#
|
1540
|
-
# def type_name(pre_type)
|
1541
|
-
# pre_type.const_name + if pre_type.variables?
|
1542
|
-
# '[' + pre_type.variables.map { |v| @variable_mapping[v] } * ',' + ']'
|
1543
|
-
# else
|
1544
|
-
# ''
|
1545
|
-
# end
|
1546
|
-
# end
|
1547
|
-
# end
|
1548
|
-
#
|
1549
|
-
# class Environment
|
1550
|
-
# attr_reader :pre_types
|
1551
|
-
# def initialize(&definition)
|
1552
|
-
# @pre_types = {}
|
1553
|
-
# @types = {}
|
1554
|
-
# instance_eval &definition
|
1555
|
-
# end
|
1556
|
-
#
|
1557
|
-
# def method_missing(method, *fields, &definition)
|
1558
|
-
# name = method.to_s
|
1559
|
-
# @pre_types[name] ||= PreType.new(self, name)
|
1560
|
-
# @pre_types[name].fields = fields unless fields.empty?
|
1561
|
-
# @pre_types[name].definition = definition if definition
|
1562
|
-
# @pre_types[name]
|
1563
|
-
# end
|
1564
|
-
#
|
1565
|
-
# def run
|
1566
|
-
# pre_types = @pre_types.select { |_, pt| pt.no_variables? }
|
1567
|
-
# types = TypeFactory.new(pre_types, {}).define
|
1568
|
-
# constants = pre_types.inject({}) do |hash, (name, pre_type)|
|
1569
|
-
# hash.update pre_type.const_name => types[name]
|
1570
|
-
# end
|
1571
|
-
#
|
1572
|
-
# pre_constructors = @pre_types.select { |_, pt| pt.variables? }
|
1573
|
-
# constructors = pre_constructors.inject({}) do |hash, (name, pt)|
|
1574
|
-
# pre_types = Hash[([pt] + pt.dependent).map { |pt| [pt.name, pt] }]
|
1575
|
-
# hash.update name => TypeConstructor.new(pre_types, pt.const_name, pt.variables)
|
1576
|
-
# end
|
1577
|
-
#
|
1578
|
-
# constants.update Hash[constructors.map { |name, c| [c.const_name, c] }]
|
1579
|
-
#
|
1580
|
-
# @pre_types.map { |name, pre_type| types[name] || constructors[name] || raise("missing #{name}") }
|
1581
|
-
# end
|
1582
|
-
#
|
1583
|
-
# private
|
1584
|
-
#
|
1585
|
-
# #def define_constants(map)
|
1586
|
-
# # map.each { |const_name, value| @base.const_set const_name.to_sym, value } if @base
|
1587
|
-
# #end
|
1588
|
-
# end
|
1589
|
-
#
|
1590
|
-
# def type_def(&definition)
|
1591
|
-
# Environment.new(&definition).run
|
1592
|
-
# end
|
1593
|
-
#end
|
1594
|
-
|
1595
|
-
#extend DSL
|
1596
1126
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: algebrick
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Petr Chalupa
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-10-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -81,7 +81,7 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: kramdown
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - '>='
|
@@ -127,14 +127,14 @@ email: git@pitr.ch
|
|
127
127
|
executables: []
|
128
128
|
extensions: []
|
129
129
|
extra_rdoc_files:
|
130
|
-
- LICENSE
|
130
|
+
- LICENSE.txt
|
131
131
|
- README.md
|
132
132
|
- README_FULL.md
|
133
133
|
- VERSION
|
134
134
|
files:
|
135
135
|
- lib/algebrick.rb
|
136
136
|
- VERSION
|
137
|
-
- LICENSE
|
137
|
+
- LICENSE.txt
|
138
138
|
- README.md
|
139
139
|
- README_FULL.md
|
140
140
|
homepage: https://github.com/pitr-ch/algebrick
|