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