fear 0.11.0 → 1.0.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 +5 -5
- data/.gitignore +0 -1
- data/.rubocop.yml +18 -0
- data/.travis.yml +0 -3
- data/CHANGELOG.md +12 -1
- data/Gemfile +1 -0
- data/{gemfiles/dry_equalizer_0.2.1.gemfile.lock → Gemfile.lock} +21 -12
- data/README.md +594 -241
- data/Rakefile +166 -219
- data/benchmarks/README.md +1 -0
- data/benchmarks/dry_do_vs_fear_for.txt +11 -0
- data/benchmarks/dry_some_fmap_vs_fear_some_map.txt +11 -0
- data/benchmarks/factorial.txt +16 -0
- data/benchmarks/fear_gaurd_and1_vs_new.txt +13 -0
- data/benchmarks/fear_gaurd_and2_vs_and.txt +13 -0
- data/benchmarks/fear_gaurd_and3_vs_and_and.txt +13 -0
- data/benchmarks/fear_pattern_extracting_with_vs_without_cache.txt +11 -0
- data/benchmarks/fear_pattern_matching_construction_vs_execution.txt +13 -0
- data/benchmarks/pattern_matching_dry_vs_qo_vs_fear_try.txt +14 -0
- data/benchmarks/pattern_matching_qo_vs_fear_pattern_extraction.txt +11 -0
- data/benchmarks/pattern_matching_qo_vs_fear_try_execution.txt +11 -0
- data/examples/pattern_extracting.rb +15 -0
- data/examples/pattern_matching_binary_tree_set.rb +96 -0
- data/examples/pattern_matching_number_in_words.rb +54 -0
- data/fear.gemspec +4 -2
- data/lib/fear.rb +21 -4
- data/lib/fear/either.rb +77 -59
- data/lib/fear/either_api.rb +21 -0
- data/lib/fear/empty_partial_function.rb +1 -1
- data/lib/fear/extractor.rb +108 -0
- data/lib/fear/extractor/anonymous_array_splat_matcher.rb +8 -0
- data/lib/fear/extractor/any_matcher.rb +15 -0
- data/lib/fear/extractor/array_head_matcher.rb +34 -0
- data/lib/fear/extractor/array_matcher.rb +38 -0
- data/lib/fear/extractor/array_splat_matcher.rb +14 -0
- data/lib/fear/extractor/empty_list_matcher.rb +18 -0
- data/lib/fear/extractor/extractor_matcher.rb +42 -0
- data/lib/fear/extractor/grammar.rb +201 -0
- data/lib/fear/extractor/grammar.treetop +129 -0
- data/lib/fear/extractor/identifier_matcher.rb +16 -0
- data/lib/fear/extractor/matcher.rb +54 -0
- data/lib/fear/extractor/matcher/and.rb +36 -0
- data/lib/fear/extractor/named_array_splat_matcher.rb +15 -0
- data/lib/fear/extractor/pattern.rb +55 -0
- data/lib/fear/extractor/typed_identifier_matcher.rb +24 -0
- data/lib/fear/extractor/value_matcher.rb +17 -0
- data/lib/fear/extractor_api.rb +33 -0
- data/lib/fear/failure.rb +32 -10
- data/lib/fear/for.rb +14 -69
- data/lib/fear/for_api.rb +66 -0
- data/lib/fear/future.rb +414 -0
- data/lib/fear/future_api.rb +19 -0
- data/lib/fear/left.rb +8 -0
- data/lib/fear/none.rb +17 -8
- data/lib/fear/option.rb +55 -49
- data/lib/fear/option_api.rb +38 -0
- data/lib/fear/partial_function.rb +9 -12
- data/lib/fear/partial_function/empty.rb +1 -1
- data/lib/fear/partial_function/guard.rb +8 -20
- data/lib/fear/partial_function/lifted.rb +1 -0
- data/lib/fear/partial_function_class.rb +10 -0
- data/lib/fear/pattern_match.rb +10 -0
- data/lib/fear/pattern_matching_api.rb +35 -11
- data/lib/fear/promise.rb +87 -0
- data/lib/fear/right.rb +8 -0
- data/lib/fear/some.rb +22 -3
- data/lib/fear/success.rb +22 -1
- data/lib/fear/try.rb +82 -67
- data/lib/fear/try_api.rb +31 -0
- data/lib/fear/unit.rb +28 -0
- data/lib/fear/version.rb +1 -1
- data/spec/fear/done_spec.rb +3 -3
- data/spec/fear/either/mixin_spec.rb +15 -0
- data/spec/fear/either_pattern_match_spec.rb +10 -12
- data/spec/fear/extractor/array_matcher_spec.rb +228 -0
- data/spec/fear/extractor/extractor_matcher_spec.rb +151 -0
- data/spec/fear/extractor/grammar_array_spec.rb +23 -0
- data/spec/fear/extractor/identified_matcher_spec.rb +47 -0
- data/spec/fear/extractor/identifier_matcher_spec.rb +66 -0
- data/spec/fear/extractor/pattern_spec.rb +32 -0
- data/spec/fear/extractor/typed_identifier_matcher_spec.rb +62 -0
- data/spec/fear/extractor/value_matcher_number_spec.rb +77 -0
- data/spec/fear/extractor/value_matcher_string_spec.rb +86 -0
- data/spec/fear/extractor/value_matcher_symbol_spec.rb +69 -0
- data/spec/fear/extractor_api_spec.rb +113 -0
- data/spec/fear/extractor_spec.rb +59 -0
- data/spec/fear/failure_spec.rb +73 -13
- data/spec/fear/for_spec.rb +35 -35
- data/spec/fear/future_spec.rb +466 -0
- data/spec/fear/guard_spec.rb +4 -4
- data/spec/fear/left_spec.rb +40 -14
- data/spec/fear/none_spec.rb +28 -12
- data/spec/fear/option/mixin_spec.rb +37 -0
- data/spec/fear/option_pattern_match_spec.rb +7 -9
- data/spec/fear/partial_function_spec.rb +25 -3
- data/spec/fear/pattern_match_spec.rb +33 -1
- data/spec/fear/promise_spec.rb +94 -0
- data/spec/fear/right_spec.rb +37 -9
- data/spec/fear/some_spec.rb +32 -6
- data/spec/fear/success_spec.rb +32 -4
- data/spec/fear/try/mixin_spec.rb +17 -0
- data/spec/fear/try_pattern_match_spec.rb +8 -10
- data/spec/spec_helper.rb +1 -1
- metadata +115 -20
- data/Appraisals +0 -32
- data/gemfiles/dry_equalizer_0.1.0.gemfile +0 -8
- data/gemfiles/dry_equalizer_0.1.0.gemfile.lock +0 -82
- data/gemfiles/dry_equalizer_0.2.1.gemfile +0 -8
- data/lib/fear/done.rb +0 -22
- data/spec/fear/option_spec.rb +0 -15
@@ -0,0 +1,21 @@
|
|
1
|
+
module Fear
|
2
|
+
module EitherApi
|
3
|
+
# @param value [any]
|
4
|
+
# @return [Fear::Left]
|
5
|
+
# @example
|
6
|
+
# Fear.left(42) #=> #<Fear::Left value=42>
|
7
|
+
#
|
8
|
+
def left(value)
|
9
|
+
Fear::Left.new(value)
|
10
|
+
end
|
11
|
+
|
12
|
+
# @param value [any]
|
13
|
+
# @return [Fear::Right]
|
14
|
+
# @example
|
15
|
+
# Fear.right(42) #=> #<Fear::Right value=42>
|
16
|
+
#
|
17
|
+
def right(value)
|
18
|
+
Fear::Right.new(value)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'treetop'
|
2
|
+
require 'fear/extractor/grammar'
|
3
|
+
Treetop.load File.expand_path('extractor/grammar.treetop', __dir__)
|
4
|
+
|
5
|
+
module Fear
|
6
|
+
# @api private
|
7
|
+
module Extractor
|
8
|
+
autoload :Pattern, 'fear/extractor/pattern'
|
9
|
+
autoload :Matcher, 'fear/extractor/matcher'
|
10
|
+
|
11
|
+
autoload :AnonymousArraySplatMatcher, 'fear/extractor/anonymous_array_splat_matcher'
|
12
|
+
autoload :AnyMatcher, 'fear/extractor/any_matcher'
|
13
|
+
autoload :ArrayHeadMatcher, 'fear/extractor/array_head_matcher'
|
14
|
+
autoload :ArrayMatcher, 'fear/extractor/array_matcher'
|
15
|
+
autoload :ArraySplatMatcher, 'fear/extractor/array_splat_matcher'
|
16
|
+
autoload :EmptyListMatcher, 'fear/extractor/empty_list_matcher'
|
17
|
+
autoload :ExtractorMatcher, 'fear/extractor/extractor_matcher'
|
18
|
+
autoload :IdentifierMatcher, 'fear/extractor/identifier_matcher'
|
19
|
+
autoload :NamedArraySplatMatcher, 'fear/extractor/named_array_splat_matcher'
|
20
|
+
autoload :TypedIdentifierMatcher, 'fear/extractor/typed_identifier_matcher'
|
21
|
+
autoload :ValueMatcher, 'fear/extractor/value_matcher'
|
22
|
+
|
23
|
+
ExtractorNotFound = Class.new(Error)
|
24
|
+
|
25
|
+
@mutex = Mutex.new
|
26
|
+
@registry = PartialFunction::EMPTY
|
27
|
+
|
28
|
+
EXTRACTOR_NOT_FOUND = proc do |klass|
|
29
|
+
raise ExtractorNotFound, 'could not find extractor for ' + klass.inspect
|
30
|
+
end
|
31
|
+
|
32
|
+
class << self
|
33
|
+
# @param klass [Class, String]
|
34
|
+
# @api private
|
35
|
+
def find_extractor(klass)
|
36
|
+
@registry.call_or_else(klass, &EXTRACTOR_NOT_FOUND)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Register extractor for given class
|
40
|
+
# @!method register_extractor(*names, extractor)
|
41
|
+
# @param names [<Class, String>, Class, String] name of a class. You can also pass alias for the name
|
42
|
+
# @param extractor [Proc<any => Fear::Option>] proc taking any argument and returned Option
|
43
|
+
# of extracted value('s)
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
# register_extractor(Fear::Some, Fear.case(Fear::Some) { |some| some.get }.lift)
|
47
|
+
#
|
48
|
+
# register_extractor(User, Fear.case(User) { |user|} [user.id, user.email] , )
|
49
|
+
#
|
50
|
+
# @example registering an alias. Alias should be CamelCased string
|
51
|
+
# register_extractor(Fear::Some, 'Some', Fear.case(Fear::Some) { |some| some.get }.lift)
|
52
|
+
#
|
53
|
+
# # no you can extract Fear::Some's using Some alias
|
54
|
+
# m.case(Fear['Some(value : Integer)']) { |value:| value * 2 }
|
55
|
+
#
|
56
|
+
def register_extractor(*args)
|
57
|
+
*keys, extractor = *args
|
58
|
+
|
59
|
+
@mutex.synchronize do
|
60
|
+
keys.uniq.each do |key|
|
61
|
+
@registry = BUILD_EXTRACTOR.call(key, extractor).or_else(@registry)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
BUILD_EXTRACTOR = proc do |key, extractor|
|
68
|
+
Fear.matcher do |m|
|
69
|
+
case key
|
70
|
+
when String
|
71
|
+
m.case(Module, ->(lookup) { lookup.to_s == key }) { extractor }
|
72
|
+
m.case(String, key) { extractor }
|
73
|
+
when Module
|
74
|
+
m.case(Module, ->(lookup) { lookup <= key }) { extractor }
|
75
|
+
m.case(String, key.to_s) { extractor }
|
76
|
+
else
|
77
|
+
m.case(key) { extractor } # may it be useful to register other types of keys? lambda?
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Multiple arguments extractor example
|
84
|
+
register_extractor('Date', proc do |other|
|
85
|
+
if other.class.name == 'Date'
|
86
|
+
Fear.some([other.year, other.month, other.day])
|
87
|
+
else
|
88
|
+
Fear.none
|
89
|
+
end
|
90
|
+
end)
|
91
|
+
register_extractor(Struct, Fear.case(Struct, &:to_a).lift)
|
92
|
+
# No argument boolean extractor example
|
93
|
+
register_extractor('IsEven', proc do |int|
|
94
|
+
if int.is_a?(Integer) && int.even?
|
95
|
+
Fear.some([])
|
96
|
+
else
|
97
|
+
Fear.none
|
98
|
+
end
|
99
|
+
end)
|
100
|
+
# Single argument extractor example
|
101
|
+
register_extractor('Fear::Some', 'Some', Some::EXTRACTOR)
|
102
|
+
register_extractor('Fear::None', 'None', NoneClass::EXTRACTOR)
|
103
|
+
register_extractor('Fear::Right', 'Right', Right::EXTRACTOR)
|
104
|
+
register_extractor('Fear::Left', 'Left', Left::EXTRACTOR)
|
105
|
+
register_extractor('Fear::Success', 'Success', Success::EXTRACTOR)
|
106
|
+
register_extractor('Fear::Failure', 'Failure', Failure::EXTRACTOR)
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Fear
|
2
|
+
module Extractor
|
3
|
+
# Part of recursive array matcher. Match against its head.
|
4
|
+
# @see ArrayMatcher
|
5
|
+
class ArrayHeadMatcher < Matcher
|
6
|
+
# @!attribute matcher
|
7
|
+
# @return [Matcher]
|
8
|
+
# @!attribute index
|
9
|
+
# @return [Types::Strict::Integer]
|
10
|
+
|
11
|
+
# @param other [<>]
|
12
|
+
def defined_at?(other)
|
13
|
+
if other.empty?
|
14
|
+
false
|
15
|
+
else
|
16
|
+
matcher.defined_at?(other.first)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param other [<>]
|
21
|
+
def bindings(other)
|
22
|
+
if other.empty?
|
23
|
+
super
|
24
|
+
else
|
25
|
+
matcher.bindings(other.first)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def failure_reason(other)
|
30
|
+
matcher.failure_reason(other.first)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Fear
|
2
|
+
module Extractor
|
3
|
+
# Recursive array matcher. Match against its head and tail
|
4
|
+
#
|
5
|
+
class ArrayMatcher < Matcher
|
6
|
+
# @!attribute head
|
7
|
+
# @return [ArrayHeadMatcher]
|
8
|
+
# @!attribute tail
|
9
|
+
# @return [ArrayMatcher | EmptyListMatcher]
|
10
|
+
|
11
|
+
def defined_at?(other)
|
12
|
+
if other.is_a?(Array)
|
13
|
+
head.defined_at?(other) && tail.defined_at?(other.slice(1..-1))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def bindings(other)
|
18
|
+
if head.is_a?(ArraySplatMatcher)
|
19
|
+
head.bindings(other)
|
20
|
+
else
|
21
|
+
head.bindings(other).merge(tail.bindings(other.slice(1..-1)))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def failure_reason(other)
|
26
|
+
if other.is_a?(Array)
|
27
|
+
if head.defined_at?(other)
|
28
|
+
tail.failure_reason(other.slice(1..-1))
|
29
|
+
else
|
30
|
+
head.failure_reason(other)
|
31
|
+
end
|
32
|
+
else
|
33
|
+
super
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Fear
|
2
|
+
module Extractor
|
3
|
+
# Match only if array is empty
|
4
|
+
#
|
5
|
+
class EmptyListMatcher < Matcher
|
6
|
+
# @!attribute index
|
7
|
+
# @return [Types::Strict::Integer]
|
8
|
+
#
|
9
|
+
def defined_at?(other)
|
10
|
+
other.empty?
|
11
|
+
end
|
12
|
+
|
13
|
+
def bindings(_)
|
14
|
+
EMPTY_HASH
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Fear
|
2
|
+
module Extractor
|
3
|
+
# Match and extract pattern using registered extractor objects
|
4
|
+
# E.g. +Some(a : Integer)+
|
5
|
+
# @see Extractor.register_extractor
|
6
|
+
class ExtractorMatcher < Matcher
|
7
|
+
# @!attribute name
|
8
|
+
# @return [Types::Strict::String]
|
9
|
+
# @!attribute arguments_matcher
|
10
|
+
# @return [ArrayMatcher | EmptyListMatcher]
|
11
|
+
#
|
12
|
+
|
13
|
+
def initialize(*)
|
14
|
+
super
|
15
|
+
@extractor = Extractor.find_extractor(name)
|
16
|
+
end
|
17
|
+
attr_reader :extractor
|
18
|
+
private :extractor
|
19
|
+
|
20
|
+
def defined_at?(other)
|
21
|
+
extractor
|
22
|
+
.call(other)
|
23
|
+
.map { |v| arguments_matcher.defined_at?(v) }
|
24
|
+
.get_or_else(false)
|
25
|
+
end
|
26
|
+
|
27
|
+
def call_or_else(arg)
|
28
|
+
extractor.call(arg)
|
29
|
+
.map { |v| arguments_matcher.call_or_else(v) { yield arg } }
|
30
|
+
.get_or_else { yield arg }
|
31
|
+
end
|
32
|
+
|
33
|
+
def failure_reason(other)
|
34
|
+
extractor.call(other).match do |m|
|
35
|
+
m.some(->(v) { arguments_matcher.defined_at?(v) }) { Fear.none }
|
36
|
+
m.some { |v| arguments_matcher.failure_reason(v) }
|
37
|
+
m.none { super }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
module Fear
|
2
|
+
module Extractor
|
3
|
+
# This module contains AST nodes for GrammarParser
|
4
|
+
# generated by +treetop+. The sole purpose of them all is to
|
5
|
+
# generate matchers
|
6
|
+
module Grammar
|
7
|
+
class Node < Treetop::Runtime::SyntaxNode
|
8
|
+
end
|
9
|
+
|
10
|
+
class EmptyArray < Node
|
11
|
+
def to_matcher
|
12
|
+
EmptyListMatcher.new(node: self)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class ArrayLiteral < Node
|
17
|
+
def to_matcher
|
18
|
+
elements[1].to_matcher
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class NonEmptyArray < Node
|
23
|
+
def to_matcher
|
24
|
+
head, tail = elements
|
25
|
+
ArrayMatcher.new(head: head.to_matcher, tail: tail.to_matcher, node: self)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class ArrayTail < Node
|
30
|
+
def to_matcher
|
31
|
+
elements[1].to_matcher
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class ArrayHead < Node
|
36
|
+
def to_matcher
|
37
|
+
ArrayHeadMatcher.new(matcher: elements[1].to_matcher, node: elements[1])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class ArraySplat < Node
|
42
|
+
def to_matcher
|
43
|
+
elements[1].to_matcher
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class AnonymousArraySplat < Node
|
48
|
+
def to_matcher
|
49
|
+
AnonymousArraySplatMatcher.new(node: self)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class FloatLiteral < Node
|
54
|
+
def to_matcher
|
55
|
+
ValueMatcher.new(value: value, node: self)
|
56
|
+
end
|
57
|
+
|
58
|
+
def value
|
59
|
+
text_value.to_f
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class IntegerLiteral < Node
|
64
|
+
def to_matcher
|
65
|
+
ValueMatcher.new(value: value, node: self)
|
66
|
+
end
|
67
|
+
|
68
|
+
def value
|
69
|
+
text_value.to_i
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class StringLiteral < Node
|
74
|
+
def to_matcher
|
75
|
+
ValueMatcher.new(value: value, node: self)
|
76
|
+
end
|
77
|
+
|
78
|
+
def value
|
79
|
+
elements[1].text_value
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
require 'yaml'
|
84
|
+
|
85
|
+
class DoubleQuotedStringLiteral < StringLiteral
|
86
|
+
def to_matcher
|
87
|
+
ValueMatcher.new(value: value, node: self)
|
88
|
+
end
|
89
|
+
|
90
|
+
def value
|
91
|
+
YAML.safe_load(%(---\n"#{super}"\n))
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class SymbolLiteral < Node
|
96
|
+
def to_matcher
|
97
|
+
ValueMatcher.new(value: value, node: self)
|
98
|
+
end
|
99
|
+
|
100
|
+
def value
|
101
|
+
elements[1].value.to_sym
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class TrueLiteral < Node
|
106
|
+
def to_matcher
|
107
|
+
ValueMatcher.new(value: true, node: self)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class FalseLiteral < Node
|
112
|
+
def to_matcher
|
113
|
+
ValueMatcher.new(value: false, node: self)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class NilLiteral < Node
|
118
|
+
def to_matcher
|
119
|
+
ValueMatcher.new(value: nil, node: self)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class AnyIdentifier < Node
|
124
|
+
def to_matcher
|
125
|
+
AnyMatcher.new(node: self)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class Identifier < Node
|
130
|
+
def to_matcher
|
131
|
+
IdentifierMatcher.new(name: value, node: self)
|
132
|
+
end
|
133
|
+
|
134
|
+
def value
|
135
|
+
text_value.to_sym
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class NamedArraySplat < Node
|
140
|
+
def to_matcher
|
141
|
+
NamedArraySplatMatcher.new(name: name, node: self)
|
142
|
+
end
|
143
|
+
|
144
|
+
def name
|
145
|
+
text_value[1..-1].to_sym
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class TypeLiteral < Node
|
150
|
+
def to_matcher
|
151
|
+
ValueMatcher.new(value: value, node: self)
|
152
|
+
end
|
153
|
+
|
154
|
+
def value
|
155
|
+
Object.const_get(text_value)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
class TypedIdentifier < Node
|
160
|
+
def to_matcher
|
161
|
+
identifier, type = elements.values_at(0, 2)
|
162
|
+
type.to_matcher.and(identifier.to_matcher)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
class IdentifiedMatcher < Node
|
167
|
+
def to_matcher
|
168
|
+
identifier, matcher = elements.values_at(0, -1)
|
169
|
+
identifier.to_matcher.and(matcher.to_matcher)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
class ExtractorLiteral < Node
|
174
|
+
def to_matcher
|
175
|
+
ExtractorMatcher.new(
|
176
|
+
name: extractor_name,
|
177
|
+
arguments_matcher: extractor_arguments,
|
178
|
+
node: self,
|
179
|
+
)
|
180
|
+
end
|
181
|
+
|
182
|
+
private def extractor_name
|
183
|
+
name = elements[0].text_value
|
184
|
+
if Object.const_defined?(name)
|
185
|
+
Object.const_get(name)
|
186
|
+
else
|
187
|
+
name
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
private def extractor_arguments
|
192
|
+
if elements[2].empty?
|
193
|
+
EmptyListMatcher.new(node: self)
|
194
|
+
else
|
195
|
+
elements[2].to_matcher
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|