fear 0.11.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +0 -1
  3. data/.rubocop.yml +18 -0
  4. data/.travis.yml +0 -3
  5. data/CHANGELOG.md +12 -1
  6. data/Gemfile +1 -0
  7. data/{gemfiles/dry_equalizer_0.2.1.gemfile.lock → Gemfile.lock} +21 -12
  8. data/README.md +594 -241
  9. data/Rakefile +166 -219
  10. data/benchmarks/README.md +1 -0
  11. data/benchmarks/dry_do_vs_fear_for.txt +11 -0
  12. data/benchmarks/dry_some_fmap_vs_fear_some_map.txt +11 -0
  13. data/benchmarks/factorial.txt +16 -0
  14. data/benchmarks/fear_gaurd_and1_vs_new.txt +13 -0
  15. data/benchmarks/fear_gaurd_and2_vs_and.txt +13 -0
  16. data/benchmarks/fear_gaurd_and3_vs_and_and.txt +13 -0
  17. data/benchmarks/fear_pattern_extracting_with_vs_without_cache.txt +11 -0
  18. data/benchmarks/fear_pattern_matching_construction_vs_execution.txt +13 -0
  19. data/benchmarks/pattern_matching_dry_vs_qo_vs_fear_try.txt +14 -0
  20. data/benchmarks/pattern_matching_qo_vs_fear_pattern_extraction.txt +11 -0
  21. data/benchmarks/pattern_matching_qo_vs_fear_try_execution.txt +11 -0
  22. data/examples/pattern_extracting.rb +15 -0
  23. data/examples/pattern_matching_binary_tree_set.rb +96 -0
  24. data/examples/pattern_matching_number_in_words.rb +54 -0
  25. data/fear.gemspec +4 -2
  26. data/lib/fear.rb +21 -4
  27. data/lib/fear/either.rb +77 -59
  28. data/lib/fear/either_api.rb +21 -0
  29. data/lib/fear/empty_partial_function.rb +1 -1
  30. data/lib/fear/extractor.rb +108 -0
  31. data/lib/fear/extractor/anonymous_array_splat_matcher.rb +8 -0
  32. data/lib/fear/extractor/any_matcher.rb +15 -0
  33. data/lib/fear/extractor/array_head_matcher.rb +34 -0
  34. data/lib/fear/extractor/array_matcher.rb +38 -0
  35. data/lib/fear/extractor/array_splat_matcher.rb +14 -0
  36. data/lib/fear/extractor/empty_list_matcher.rb +18 -0
  37. data/lib/fear/extractor/extractor_matcher.rb +42 -0
  38. data/lib/fear/extractor/grammar.rb +201 -0
  39. data/lib/fear/extractor/grammar.treetop +129 -0
  40. data/lib/fear/extractor/identifier_matcher.rb +16 -0
  41. data/lib/fear/extractor/matcher.rb +54 -0
  42. data/lib/fear/extractor/matcher/and.rb +36 -0
  43. data/lib/fear/extractor/named_array_splat_matcher.rb +15 -0
  44. data/lib/fear/extractor/pattern.rb +55 -0
  45. data/lib/fear/extractor/typed_identifier_matcher.rb +24 -0
  46. data/lib/fear/extractor/value_matcher.rb +17 -0
  47. data/lib/fear/extractor_api.rb +33 -0
  48. data/lib/fear/failure.rb +32 -10
  49. data/lib/fear/for.rb +14 -69
  50. data/lib/fear/for_api.rb +66 -0
  51. data/lib/fear/future.rb +414 -0
  52. data/lib/fear/future_api.rb +19 -0
  53. data/lib/fear/left.rb +8 -0
  54. data/lib/fear/none.rb +17 -8
  55. data/lib/fear/option.rb +55 -49
  56. data/lib/fear/option_api.rb +38 -0
  57. data/lib/fear/partial_function.rb +9 -12
  58. data/lib/fear/partial_function/empty.rb +1 -1
  59. data/lib/fear/partial_function/guard.rb +8 -20
  60. data/lib/fear/partial_function/lifted.rb +1 -0
  61. data/lib/fear/partial_function_class.rb +10 -0
  62. data/lib/fear/pattern_match.rb +10 -0
  63. data/lib/fear/pattern_matching_api.rb +35 -11
  64. data/lib/fear/promise.rb +87 -0
  65. data/lib/fear/right.rb +8 -0
  66. data/lib/fear/some.rb +22 -3
  67. data/lib/fear/success.rb +22 -1
  68. data/lib/fear/try.rb +82 -67
  69. data/lib/fear/try_api.rb +31 -0
  70. data/lib/fear/unit.rb +28 -0
  71. data/lib/fear/version.rb +1 -1
  72. data/spec/fear/done_spec.rb +3 -3
  73. data/spec/fear/either/mixin_spec.rb +15 -0
  74. data/spec/fear/either_pattern_match_spec.rb +10 -12
  75. data/spec/fear/extractor/array_matcher_spec.rb +228 -0
  76. data/spec/fear/extractor/extractor_matcher_spec.rb +151 -0
  77. data/spec/fear/extractor/grammar_array_spec.rb +23 -0
  78. data/spec/fear/extractor/identified_matcher_spec.rb +47 -0
  79. data/spec/fear/extractor/identifier_matcher_spec.rb +66 -0
  80. data/spec/fear/extractor/pattern_spec.rb +32 -0
  81. data/spec/fear/extractor/typed_identifier_matcher_spec.rb +62 -0
  82. data/spec/fear/extractor/value_matcher_number_spec.rb +77 -0
  83. data/spec/fear/extractor/value_matcher_string_spec.rb +86 -0
  84. data/spec/fear/extractor/value_matcher_symbol_spec.rb +69 -0
  85. data/spec/fear/extractor_api_spec.rb +113 -0
  86. data/spec/fear/extractor_spec.rb +59 -0
  87. data/spec/fear/failure_spec.rb +73 -13
  88. data/spec/fear/for_spec.rb +35 -35
  89. data/spec/fear/future_spec.rb +466 -0
  90. data/spec/fear/guard_spec.rb +4 -4
  91. data/spec/fear/left_spec.rb +40 -14
  92. data/spec/fear/none_spec.rb +28 -12
  93. data/spec/fear/option/mixin_spec.rb +37 -0
  94. data/spec/fear/option_pattern_match_spec.rb +7 -9
  95. data/spec/fear/partial_function_spec.rb +25 -3
  96. data/spec/fear/pattern_match_spec.rb +33 -1
  97. data/spec/fear/promise_spec.rb +94 -0
  98. data/spec/fear/right_spec.rb +37 -9
  99. data/spec/fear/some_spec.rb +32 -6
  100. data/spec/fear/success_spec.rb +32 -4
  101. data/spec/fear/try/mixin_spec.rb +17 -0
  102. data/spec/fear/try_pattern_match_spec.rb +8 -10
  103. data/spec/spec_helper.rb +1 -1
  104. metadata +115 -20
  105. data/Appraisals +0 -32
  106. data/gemfiles/dry_equalizer_0.1.0.gemfile +0 -8
  107. data/gemfiles/dry_equalizer_0.1.0.gemfile.lock +0 -82
  108. data/gemfiles/dry_equalizer_0.2.1.gemfile +0 -8
  109. data/lib/fear/done.rb +0 -22
  110. data/spec/fear/option_spec.rb +0 -15
@@ -0,0 +1,23 @@
1
+ RSpec.describe Fear::Extractor::Grammar, 'Array' do
2
+ let(:parser) { Fear::Extractor::GrammarParser.new }
3
+ let(:matcher) { parser.parse(pattern).to_matcher }
4
+
5
+ context 'non empty array' do
6
+ let(:pattern) { '[1, 2, 3, 4]' }
7
+
8
+ it do
9
+ first = matcher.head
10
+ rest_after_first = matcher.tail
11
+
12
+ expect(first).to be_kind_of(Fear::Extractor::ArrayHeadMatcher)
13
+ expect(first.matcher.value).to eq(1)
14
+ expect(rest_after_first).to be_kind_of(Fear::Extractor::ArrayMatcher)
15
+
16
+ second = rest_after_first.head
17
+ rest_after_second = rest_after_first.tail
18
+ expect(second).to be_kind_of(Fear::Extractor::ArrayHeadMatcher)
19
+ expect(second.matcher.value).to eq(2)
20
+ expect(rest_after_second).to be_kind_of(Fear::Extractor::ArrayMatcher)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,47 @@
1
+ RSpec.describe 'Fear::Extractor::IdentifiedMatcher' do
2
+ let(:parser) { Fear::Extractor::GrammarParser.new }
3
+ let(:matcher) { parser.parse(pattern).to_matcher }
4
+
5
+ describe '#defined_at?' do
6
+ subject { matcher }
7
+
8
+ let(:pattern) { 'array @ [1, *tail]' }
9
+
10
+ it { is_expected.to be_defined_at([1, 2]) }
11
+ it { is_expected.not_to be_defined_at('foo') }
12
+ it { is_expected.not_to be_defined_at([2, 1]) }
13
+ end
14
+
15
+ describe '#call' do
16
+ subject { matcher.call(other) }
17
+
18
+ context 'defined' do
19
+ let(:other) { [1, 2] }
20
+ let(:pattern) { 'array @ [1, *tail]' }
21
+
22
+ it { is_expected.to eq(array: [1, 2], tail: [2]) }
23
+ end
24
+ end
25
+
26
+ describe '#failure_reason' do
27
+ subject { matcher.failure_reason(other) }
28
+
29
+ let(:pattern) { 'array @ [1, *tail]' }
30
+
31
+ context 'match integer' do
32
+ let(:other) { [1, 2] }
33
+
34
+ it { is_expected.to eq(Fear.none) }
35
+ end
36
+
37
+ context 'does not match float' do
38
+ let(:other) { [2, 2] }
39
+
40
+ it { is_expected.to eq(Fear.some(<<-ERROR.strip)) }
41
+ Expected `2` to match:
42
+ array @ [1, *tail]
43
+ ~~~~~~~~~^
44
+ ERROR
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,66 @@
1
+ RSpec.describe Fear::Extractor::IdentifierMatcher do
2
+ let(:parser) { Fear::Extractor::GrammarParser.new }
3
+ let(:matcher) { parser.parse(pattern).to_matcher }
4
+
5
+ describe '#defined_at?' do
6
+ subject { matcher }
7
+
8
+ let(:pattern) { 'number' }
9
+
10
+ it { is_expected.to be_defined_at(1) }
11
+ it { is_expected.to be_defined_at('foo') }
12
+ it { is_expected.to be_defined_at(1.2) }
13
+ it { is_expected.to be_defined_at([1, '2']) }
14
+
15
+ context 'within array' do
16
+ let(:pattern) { '[1, n, 2]' }
17
+
18
+ it { is_expected.to be_defined_at([1, 2, 2]) }
19
+ it { is_expected.to be_defined_at([1, 'foo', 2]) }
20
+ it { is_expected.not_to be_defined_at([1, 'foo']) }
21
+ end
22
+ end
23
+
24
+ describe '#call' do
25
+ subject { matcher.call(other) }
26
+
27
+ let(:pattern) { '1.0' }
28
+
29
+ context 'defined' do
30
+ let(:other) { 1 }
31
+
32
+ it { is_expected.to eq({}) }
33
+ end
34
+ end
35
+
36
+ describe '#failure_reason' do
37
+ subject { matcher.failure_reason(other) }
38
+
39
+ let(:pattern) { '1.0' }
40
+
41
+ context 'match integer' do
42
+ let(:other) { 1 }
43
+ let(:pattern) { '1' }
44
+
45
+ it { is_expected.to eq(Fear.none) }
46
+ end
47
+
48
+ context 'match float' do
49
+ let(:other) { 1.0 }
50
+ let(:pattern) { '1' }
51
+
52
+ it { is_expected.to eq(Fear.none) }
53
+ end
54
+
55
+ context 'does not match another integer' do
56
+ let(:other) { 2 }
57
+ let(:pattern) { '1' }
58
+
59
+ it { is_expected.to eq(Fear.some(<<-ERROR.strip)) }
60
+ Expected `2` to match:
61
+ 1
62
+ ^
63
+ ERROR
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,32 @@
1
+ RSpec.describe Fear::Extractor::Pattern do
2
+ describe '.new' do
3
+ context 'invalid syntax' do
4
+ subject { -> { described_class.new('[1, 2, 3') } }
5
+
6
+ it 'shows where the error happens' do
7
+ is_expected.to raise_error(Fear::PatternSyntaxError) { |error|
8
+ lines = error.message.split("\n")
9
+ expect(lines[0]).to start_with('Expected one of')
10
+ .and(end_with('at line 1, column 9 (byte 9):'))
11
+
12
+ expect(lines[1]).to eq('[1, 2, 3')
13
+ expect(lines[2]).to eq('~~~~~~~~^')
14
+ }
15
+ end
16
+ end
17
+ end
18
+
19
+ describe '#failure_reason' do
20
+ let(:pattern) { described_class.new('Some([:err, 444])') }
21
+
22
+ context 'not defined' do
23
+ subject { pattern.failure_reason(Fear.some([:err, 445])) }
24
+
25
+ it { is_expected.to eq(<<-MSG.strip) }
26
+ Expected `445` to match:
27
+ Some([:err, 444])
28
+ ~~~~~~~~~~~~^
29
+ MSG
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,62 @@
1
+ RSpec.describe Fear::Extractor::TypedIdentifierMatcher do
2
+ let(:parser) { Fear::Extractor::GrammarParser.new }
3
+ let(:matcher) { parser.parse(pattern).to_matcher }
4
+
5
+ describe '#defined_at?' do
6
+ subject { matcher }
7
+
8
+ let(:pattern) { 'var : Integer' }
9
+
10
+ it { is_expected.to be_defined_at(1) }
11
+ it { is_expected.not_to be_defined_at('foo') }
12
+ it { is_expected.not_to be_defined_at(1.2) }
13
+
14
+ context 'within array' do
15
+ let(:pattern) { '[1, n : String, 2]' }
16
+
17
+ it { is_expected.to be_defined_at([1, 'foo', 2]) }
18
+ it { is_expected.not_to be_defined_at([1, 2, 2]) }
19
+ it { is_expected.not_to be_defined_at([1, 'foo']) }
20
+ end
21
+ end
22
+
23
+ describe '#call' do
24
+ subject { matcher.call(other) }
25
+
26
+ context 'defined' do
27
+ let(:other) { 1 }
28
+ let(:pattern) { 'var : Integer' }
29
+
30
+ it { is_expected.to eq(var: 1) }
31
+ end
32
+
33
+ context 'defined within array' do
34
+ let(:other) { [4, 2, 1, 6] }
35
+ let(:pattern) { '[head : Integer, *tail]' }
36
+
37
+ it { is_expected.to eq(head: 4, tail: [2, 1, 6]) }
38
+ end
39
+ end
40
+
41
+ describe '#' do
42
+ subject { matcher.failure_reason(other) }
43
+
44
+ let(:pattern) { 'var : Integer' }
45
+
46
+ context 'match integer' do
47
+ let(:other) { 1 }
48
+
49
+ it { is_expected.to eq(Fear.none) }
50
+ end
51
+
52
+ context 'does not match float' do
53
+ let(:other) { 1.0 }
54
+
55
+ it { is_expected.to eq(Fear.some(<<-ERROR.strip)) }
56
+ Expected `1.0` to match:
57
+ var : Integer
58
+ ~~~~~~^
59
+ ERROR
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,77 @@
1
+ RSpec.describe Fear::Extractor::ValueMatcher, 'Number' do
2
+ let(:parser) { Fear::Extractor::GrammarParser.new }
3
+ let(:matcher) { parser.parse(pattern).to_matcher }
4
+
5
+ describe '#defined_at?' do
6
+ subject { matcher }
7
+
8
+ context 'Integer' do
9
+ let(:pattern) { '1' }
10
+
11
+ it { is_expected.to be_defined_at(1) }
12
+ it { is_expected.to be_defined_at(1.0) }
13
+ it { is_expected.not_to be_defined_at(2) }
14
+ it { is_expected.not_to be_defined_at('1') }
15
+ end
16
+
17
+ context 'Float' do
18
+ context 'against float' do
19
+ let(:pattern) { '1.2' }
20
+
21
+ it { is_expected.to be_defined_at(1.2) }
22
+ it { is_expected.not_to be_defined_at(1.3) }
23
+ end
24
+
25
+ context 'against integer' do
26
+ let(:pattern) { '1.0' }
27
+
28
+ it { is_expected.to be_defined_at(1) }
29
+ it { is_expected.not_to be_defined_at(2) }
30
+ it { is_expected.not_to be_defined_at('1') }
31
+ end
32
+ end
33
+ end
34
+
35
+ describe '#call' do
36
+ subject { matcher.call(other) }
37
+
38
+ let(:pattern) { '1.0' }
39
+
40
+ context 'defined' do
41
+ let(:other) { 1 }
42
+
43
+ it { is_expected.to eq({}) }
44
+ end
45
+ end
46
+
47
+ describe '#failure_reason' do
48
+ subject { matcher.failure_reason(other) }
49
+
50
+ let(:pattern) { '1.0' }
51
+
52
+ context 'match integer' do
53
+ let(:other) { 1 }
54
+ let(:pattern) { '1' }
55
+
56
+ it { is_expected.to eq(Fear.none) }
57
+ end
58
+
59
+ context 'match float' do
60
+ let(:other) { 1.0 }
61
+ let(:pattern) { '1' }
62
+
63
+ it { is_expected.to eq(Fear.none) }
64
+ end
65
+
66
+ context 'does not match another integer' do
67
+ let(:other) { 2 }
68
+ let(:pattern) { '1' }
69
+
70
+ it { is_expected.to eq(Fear.some(<<-ERROR.strip)) }
71
+ Expected `2` to match:
72
+ 1
73
+ ^
74
+ ERROR
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,86 @@
1
+ RSpec.describe Fear::Extractor::ValueMatcher, 'String' do
2
+ let(:parser) { Fear::Extractor::GrammarParser.new }
3
+ let(:matcher) { parser.parse(pattern).to_matcher }
4
+
5
+ describe '#defined_at?' do
6
+ subject { matcher }
7
+
8
+ context 'double quotas' do
9
+ let(:pattern) { %("foo") }
10
+
11
+ it { is_expected.to be_defined_at('foo') }
12
+ it { is_expected.not_to be_defined_at('boo') }
13
+ it { is_expected.not_to be_defined_at(2) }
14
+
15
+ context 'single quotes inside' do
16
+ let(:pattern) { %("f'o'o") }
17
+
18
+ it { is_expected.to be_defined_at(%(f'o'o)) }
19
+ it { is_expected.not_to be_defined_at(%(f"o"o)) }
20
+ end
21
+
22
+ context 'escaped double quotes inside' do
23
+ let(:pattern) { '"f\"oo"' }
24
+
25
+ it { is_expected.to be_defined_at('f"oo') }
26
+ it { is_expected.not_to be_defined_at("f'oo") }
27
+ end
28
+ end
29
+
30
+ context 'single quotas' do
31
+ let(:pattern) { %('foo') }
32
+
33
+ it { is_expected.to be_defined_at('foo') }
34
+ it { is_expected.not_to be_defined_at('boo') }
35
+ it { is_expected.not_to be_defined_at(2) }
36
+
37
+ context 'double quotes inside' do
38
+ let(:pattern) { %('f"o"o') }
39
+
40
+ it { is_expected.to be_defined_at(%(f"o"o)) }
41
+ it { is_expected.not_to be_defined_at(%(f'o'o)) }
42
+ end
43
+
44
+ context 'escaped single quotes inside' do
45
+ let(:pattern) { "'f\\'oo'" }
46
+
47
+ it { is_expected.to be_defined_at("f\\'oo") }
48
+ it { is_expected.not_to be_defined_at("f'oo") }
49
+ end
50
+ end
51
+ end
52
+
53
+ describe '#call' do
54
+ subject { matcher.call(other) }
55
+
56
+ let(:pattern) { '"foo"' }
57
+
58
+ context 'defined' do
59
+ let(:other) { 'foo' }
60
+
61
+ it { is_expected.to eq({}) }
62
+ end
63
+ end
64
+
65
+ describe '#failure_reason' do
66
+ subject { matcher.failure_reason(other) }
67
+
68
+ context 'match' do
69
+ let(:other) { 'foo' }
70
+ let(:pattern) { '"foo"' }
71
+
72
+ it { is_expected.to eq(Fear.none) }
73
+ end
74
+
75
+ context 'does not match' do
76
+ let(:other) { 'bar' }
77
+ let(:pattern) { '"foo"' }
78
+
79
+ it { is_expected.to eq(Fear.some(<<-ERROR.strip)) }
80
+ Expected `"bar"` to match:
81
+ "foo"
82
+ ^
83
+ ERROR
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,69 @@
1
+ RSpec.describe Fear::Extractor::ValueMatcher, 'Symbol' do
2
+ let(:parser) { Fear::Extractor::GrammarParser.new }
3
+ let(:matcher) { parser.parse(pattern).to_matcher }
4
+
5
+ describe '#defined_at?' do
6
+ subject { matcher }
7
+
8
+ context 'no quotas' do
9
+ let(:pattern) { ':foo' }
10
+
11
+ it { is_expected.to be_defined_at(:foo) }
12
+ it { is_expected.not_to be_defined_at('foo') }
13
+ it { is_expected.not_to be_defined_at(:boo) }
14
+ it { is_expected.not_to be_defined_at(2) }
15
+ end
16
+
17
+ context 'double quotas' do
18
+ let(:pattern) { %(:"foo") }
19
+
20
+ it { is_expected.to be_defined_at(:foo) }
21
+ it { is_expected.not_to be_defined_at('foo') }
22
+ it { is_expected.not_to be_defined_at(:boo) }
23
+ it { is_expected.not_to be_defined_at(2) }
24
+ end
25
+
26
+ context 'single quotas' do
27
+ let(:pattern) { %(:'foo') }
28
+
29
+ it { is_expected.to be_defined_at(:foo) }
30
+ it { is_expected.not_to be_defined_at('foo') }
31
+ it { is_expected.not_to be_defined_at(:boo) }
32
+ it { is_expected.not_to be_defined_at(2) }
33
+ end
34
+ end
35
+
36
+ describe '#call' do
37
+ subject { matcher.call(other) }
38
+
39
+ let(:pattern) { ':foo' }
40
+
41
+ context 'defined' do
42
+ let(:other) { :foo }
43
+
44
+ it { is_expected.to eq({}) }
45
+ end
46
+ end
47
+
48
+ describe '#failure_reason' do
49
+ subject { matcher.failure_reason(other) }
50
+
51
+ context 'match' do
52
+ let(:other) { :foo }
53
+ let(:pattern) { ':foo' }
54
+
55
+ it { is_expected.to eq(Fear.none) }
56
+ end
57
+
58
+ context 'does not match' do
59
+ let(:other) { :bar }
60
+ let(:pattern) { ':foo' }
61
+
62
+ it { is_expected.to eq(Fear.some(<<-ERROR.strip)) }
63
+ Expected `:bar` to match:
64
+ :foo
65
+ ^
66
+ ERROR
67
+ end
68
+ end
69
+ end