algebrick 0.2.5 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|