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