parby 0.1.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 +7 -0
- data/lib/combinators.rb +86 -0
- data/lib/parser.rb +70 -0
- data/lib/result.rb +28 -0
- data/spec/combinators_spec.rb +141 -0
- data/spec/parser_spec.rb +110 -0
- data/spec/result_spec.rb +31 -0
- data/spec/spec_helper.rb +54 -0
- metadata +65 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 86b9b440a1902cfc0bf63e95b212df61f2cd6844
|
4
|
+
data.tar.gz: 1a619e1eb0903d69a64d7db910d544ca82826370
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4dde60b0aa97baa80b0536bc73cbd77c11fb2d195a5d3042c51bc3895192102a184c15c60ff8e9849c54287d6625f3324e2cebd9c336cf1fee36efcac8617d88
|
7
|
+
data.tar.gz: 12347688dc2b62840c14d25bfe3759df98bd3d80da46fd58f43ba84ede40adfff3dec6ccc64fd97c1213eb16876c19d367fe0cd7517a4fa40c8b249c870cf289
|
data/lib/combinators.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'parser'
|
2
|
+
|
3
|
+
module Parby
|
4
|
+
# Always yields the value passed to it, no matter the input
|
5
|
+
def of(value)
|
6
|
+
Parser.new { |input, index| Success.new(index, value, input) }
|
7
|
+
end
|
8
|
+
|
9
|
+
# Yields the first character that matches predicate, it fails with the given message, otherwise a generic one
|
10
|
+
def test(predicate, description = nil)
|
11
|
+
Parser.new do |input, index|
|
12
|
+
found = nil
|
13
|
+
input.split('').each do |character|
|
14
|
+
if predicate.call(character)
|
15
|
+
found = character
|
16
|
+
break
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
if found
|
21
|
+
Success.new(index, found, input)
|
22
|
+
else
|
23
|
+
Failure.new(index, [description || 'Matching a predicate'], input)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Yields the input, if it matches the regex passed to it
|
29
|
+
def regexp(regex)
|
30
|
+
# We have to match from the beginning
|
31
|
+
real_regex = /^#{regex}/
|
32
|
+
|
33
|
+
Parser.new do |input, index|
|
34
|
+
match_data = real_regex.match(input)
|
35
|
+
|
36
|
+
if match_data.nil?
|
37
|
+
# We did not even match one letter
|
38
|
+
Failure.new(index, [regex], input)
|
39
|
+
else
|
40
|
+
Success.new(index, match_data[0], input)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Searches in the input for one of the given characters (characters can be either a string or an array), and yields it
|
46
|
+
def one_of(characters)
|
47
|
+
expected = if characters.is_a?(Array) then characters else characters.split('') end
|
48
|
+
test(Proc.new { |c| expected.include?(c) }, expected)
|
49
|
+
end
|
50
|
+
|
51
|
+
def none_of(characters)
|
52
|
+
test(Proc.new { |c| !characters.include?(c) }, ["None of #{characters}"])
|
53
|
+
end
|
54
|
+
|
55
|
+
def string(str)
|
56
|
+
Parser.new do |input, index|
|
57
|
+
furthest = -1
|
58
|
+
|
59
|
+
str.split('').each_with_index do |expected_character, expected_index|
|
60
|
+
actual_character = input[expected_index]
|
61
|
+
|
62
|
+
if actual_character.nil?
|
63
|
+
# This means that the input is smaller than the expected string
|
64
|
+
furthest = expected_index - 1
|
65
|
+
break
|
66
|
+
elsif actual_character != expected_character
|
67
|
+
# This means the input does not match exactly the string
|
68
|
+
furthest = expected_index
|
69
|
+
break
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
if furthest == -1
|
74
|
+
Success.new(index, str)
|
75
|
+
else
|
76
|
+
Failure.new(furthest, [str])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def all
|
82
|
+
Parser.new do |input, index|
|
83
|
+
Success.new(index, input, nil)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/parser.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
module Parby
|
2
|
+
class Parser
|
3
|
+
# { |input, index| } -> result or { |input| } -> result
|
4
|
+
def initialize(&block)
|
5
|
+
raise(ArgumentError, 'A bare parser must be initialized with a 1 or 2 argument block') unless block_given?
|
6
|
+
|
7
|
+
@block = block
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse(*args)
|
11
|
+
if args.length == 1
|
12
|
+
@block.call(args[0], 0)
|
13
|
+
else
|
14
|
+
@block.call(args[0], args[1])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def or(another_parser)
|
19
|
+
Parser.new do |input, index|
|
20
|
+
first = parse(input, index)
|
21
|
+
|
22
|
+
if first.failed?
|
23
|
+
another_parser.parse(input, index)
|
24
|
+
else
|
25
|
+
first
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def |(other)
|
31
|
+
self.or other
|
32
|
+
end
|
33
|
+
|
34
|
+
def and(another_parser)
|
35
|
+
Parser.new do |input, index|
|
36
|
+
first = parse(input, index)
|
37
|
+
|
38
|
+
if first.failed?
|
39
|
+
first
|
40
|
+
else
|
41
|
+
another_parser.parse(first.remaining, first.index)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def >>(other)
|
47
|
+
self.and other
|
48
|
+
end
|
49
|
+
|
50
|
+
def fmap(&block)
|
51
|
+
Parser.new do |input, index|
|
52
|
+
result = parse(input, index)
|
53
|
+
|
54
|
+
if result.succeed?
|
55
|
+
result.value = yield(result.value)
|
56
|
+
end
|
57
|
+
|
58
|
+
result
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def map(&block)
|
63
|
+
fmap(&block)
|
64
|
+
end
|
65
|
+
|
66
|
+
def consuming
|
67
|
+
self >> all
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/result.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Parby
|
2
|
+
Result = Struct.new('Result', :status, :index, :value, :furthest, :expected, :remaining) do
|
3
|
+
def failed?
|
4
|
+
!status
|
5
|
+
end
|
6
|
+
|
7
|
+
def succeed?
|
8
|
+
status
|
9
|
+
end
|
10
|
+
|
11
|
+
def completed?
|
12
|
+
remaining.nil?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Success < Result
|
17
|
+
def initialize(index, value, remaining = nil)
|
18
|
+
super(true, index, value, -1, [], remaining)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Failure < Result
|
23
|
+
def initialize(furthest, expected, remaining = nil)
|
24
|
+
actual_expected = if expected.is_a?(Array) then expected else [expected] end
|
25
|
+
super(false, -1, nil, furthest, actual_expected, remaining)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Parby, '#of' do
|
4
|
+
describe '#of' do
|
5
|
+
parser = Parby.of(2)
|
6
|
+
|
7
|
+
it 'Always succeeds' do
|
8
|
+
first_result = parser.parse('something')
|
9
|
+
second_result = parser.parse('another one')
|
10
|
+
|
11
|
+
expect(first_result.value).to eq(second_result.value)
|
12
|
+
expect(first_result).to eq(Success.new(0, 2, 'something'))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#regexp' do
|
17
|
+
parser = Parby.regexp(/[0-9]0+/)
|
18
|
+
|
19
|
+
it 'searches for a match in the input string, yields it' do
|
20
|
+
result = parser.parse('100j')
|
21
|
+
|
22
|
+
expect(result.succeed?).to be true
|
23
|
+
expect(result.completed?).to be false
|
24
|
+
expect(result.remaining).to eq('100j')
|
25
|
+
|
26
|
+
expect(result.value).to eq('100')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'fails when it does not match a regex from the start' do
|
30
|
+
result = parser.parse('j100')
|
31
|
+
|
32
|
+
expect(result.succeed?).to be false
|
33
|
+
expect(result.completed?).to be false
|
34
|
+
|
35
|
+
expect(result.expected).to eq([/[0-9]0+/])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#test' do
|
40
|
+
context 'given a predicate' do
|
41
|
+
parser = Parby.test(Proc.new { |c| c.between?('a', 'z') }, 'Alphanumeric character') # This is how Parby#between is implemented
|
42
|
+
|
43
|
+
context 'applies the predicate to each character in input' do
|
44
|
+
context 'if it matches in the first character' do
|
45
|
+
matches_first = parser.parse 'airplane'
|
46
|
+
|
47
|
+
it 'yields and does not consume the input' do
|
48
|
+
expect(matches_first.succeed?).to be true
|
49
|
+
expect(matches_first.completed?).to be false
|
50
|
+
expect(matches_first.value).to eq 'a'
|
51
|
+
expect(matches_first.remaining).to eq 'airplane'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'if it matches in the middle of the string' do
|
56
|
+
matches_in_the_middle = parser.parse '_eval'
|
57
|
+
|
58
|
+
it 'yields and does not consume the input' do
|
59
|
+
expect(matches_in_the_middle.succeed?).to be true
|
60
|
+
expect(matches_in_the_middle.completed?).to be false
|
61
|
+
expect(matches_in_the_middle.value).to eq 'e'
|
62
|
+
expect(matches_in_the_middle.remaining).to eq '_eval'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'if it does not match' do
|
67
|
+
does_not_match = parser. parse '>>='
|
68
|
+
|
69
|
+
it 'fails with the given message and does not consume the input' do
|
70
|
+
expect(does_not_match.failed?).to be true
|
71
|
+
expect(does_not_match.expected).to eq ['Alphanumeric character']
|
72
|
+
expect(does_not_match.remaining).to eq '>>='
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#one_of' do
|
80
|
+
parser = Parby.one_of('123')
|
81
|
+
|
82
|
+
it 'looks for exactly one character from the given string, and yields that character' do
|
83
|
+
result = parser.parse('jmvbn2')
|
84
|
+
|
85
|
+
expect(result.succeed?).to be true
|
86
|
+
expect(result.remaining).to eq('jmvbn2')
|
87
|
+
|
88
|
+
expect(result.value).to eq('2')
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'short circuits' do
|
92
|
+
result = parser.parse('123')
|
93
|
+
|
94
|
+
expect(result.succeed?).to be true
|
95
|
+
expect(result.remaining).to eq('123')
|
96
|
+
|
97
|
+
expect(result.value).to eq('1')
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '#none_of' do
|
102
|
+
parser = Parby.none_of '123'
|
103
|
+
|
104
|
+
it 'succeeds when it founds a character not in the string, and yields it' do
|
105
|
+
result = parser.parse '1kl'
|
106
|
+
|
107
|
+
expect(result.succeed?).to be true
|
108
|
+
expect(result.value).to eq 'k'
|
109
|
+
expect(result.remaining).to eq '1kl'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe '#string' do
|
114
|
+
parser = Parby.string('Hi!')
|
115
|
+
|
116
|
+
it 'matches whole string and consumes it' do
|
117
|
+
result = parser.parse 'Hi!'
|
118
|
+
|
119
|
+
expect(result.succeed?).to be true
|
120
|
+
expect(result.value).to eq 'Hi!'
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'Fails gracefully: says the piece of the string that it matched' do
|
124
|
+
result = parser.parse 'Hi'
|
125
|
+
|
126
|
+
expect(result.failed?).to be true
|
127
|
+
expect(result.expected).to eq ['Hi!']
|
128
|
+
expect(result.furthest).to eq 1
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe '#all' do
|
133
|
+
it 'consumes the whole input, and yields it' do
|
134
|
+
result = Parby.all.parse 42
|
135
|
+
|
136
|
+
expect(result.succeed?).to be true
|
137
|
+
expect(result.completed?).to be true
|
138
|
+
expect(result.value).to eq 42
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
data/spec/parser_spec.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Parser do
|
4
|
+
include Parby
|
5
|
+
|
6
|
+
describe '#initialize' do
|
7
|
+
context 'not passing a block' do
|
8
|
+
it 'raises' do
|
9
|
+
expect {
|
10
|
+
Parser.new(42)
|
11
|
+
}.to raise_error(ArgumentError)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'passing a block' do
|
16
|
+
it 'initializes fine' do
|
17
|
+
parser = Parser.new { |x| x }
|
18
|
+
|
19
|
+
expect(parser).to be_a(Parser)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#parse' do
|
25
|
+
it 'just calls the proc' do
|
26
|
+
parser = Parser.new { |_, _| 'Hello' }
|
27
|
+
|
28
|
+
expect(parser.parse('some text')).to eq('Hello')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#or' do
|
33
|
+
context 'Given two parsers' do
|
34
|
+
parser = Parby.regexp(/42/) | Parby.of(42)
|
35
|
+
|
36
|
+
it 'When the first one succeeds it yields, not calling the second one' do
|
37
|
+
result = parser.parse('42')
|
38
|
+
|
39
|
+
expect(result.succeed?).to be true
|
40
|
+
expect(result.value).to eq('42')
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'When the first one succeeds it calls the second one' do
|
44
|
+
result = parser.parse('lol')
|
45
|
+
|
46
|
+
expect(result.succeed?).to be true
|
47
|
+
expect(result.value).to eq(42)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#and' do
|
53
|
+
context 'Given two parsers' do
|
54
|
+
first_parser = Parby.regexp(/0/)
|
55
|
+
|
56
|
+
it 'Both should have to match to succeed, but only the second value is yielded' do
|
57
|
+
second_parser = Parby.regexp(/[0-9]+/)
|
58
|
+
parser = first_parser >> second_parser
|
59
|
+
|
60
|
+
result = parser.parse('00')
|
61
|
+
|
62
|
+
expect(result.succeed?).to be true
|
63
|
+
expect(result.value).to eq('00')
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'When the first one fails, the other one is not called' do
|
67
|
+
second_parser = spy(Parby.regexp(/[0-9]+/))
|
68
|
+
parser = first_parser >> second_parser
|
69
|
+
|
70
|
+
result = parser.parse('lol')
|
71
|
+
|
72
|
+
expect(result.failed?).to be true
|
73
|
+
expect(second_parser).to_not have_received(:parse)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#map' do
|
79
|
+
context 'Given a parser mapped with a mapping block' do
|
80
|
+
first_parser = Parser.regexp(/42/)
|
81
|
+
parser = first_parser.map(&:to_i)
|
82
|
+
|
83
|
+
it 'when the parser yields, it will apply the block to the resulting value' do
|
84
|
+
result = parser.parse('42')
|
85
|
+
|
86
|
+
expect(result.succeed?).to be true
|
87
|
+
expect(result.value).to eq(42)
|
88
|
+
end
|
89
|
+
|
90
|
+
# We still have to test for not calling the mapper function when the parser fails, but I can't mock the block :(
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#consuming' do
|
95
|
+
context 'Given a non-consuming parser' do
|
96
|
+
parser = regexp(/42/)
|
97
|
+
input = '42'
|
98
|
+
non_consuming_result = parser.parse input
|
99
|
+
|
100
|
+
it 'Converts it into a consuming one' do
|
101
|
+
consuming_result = parser.consuming.parse input
|
102
|
+
|
103
|
+
expect(consuming_result.succeed?).to be(non_consuming_result.succeed?)
|
104
|
+
expect(consuming_result.value).to eq(non_consuming_result.value)
|
105
|
+
expect(non_consuming_result.completed?).not_to be true
|
106
|
+
expect(consuming_result.completed?).to be true
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/spec/result_spec.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Result do
|
4
|
+
context 'did fail' do
|
5
|
+
result = Failure.new(10, ['stuff'])
|
6
|
+
|
7
|
+
it 'when the status is false' do
|
8
|
+
expect(result.failed?).to be true
|
9
|
+
expect(result.succeed?).to be false
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'did succeed' do
|
14
|
+
result = Success.new(1, nil)
|
15
|
+
|
16
|
+
it 'when the status is true' do
|
17
|
+
expect(result.failed?).to be false
|
18
|
+
expect(result.succeed?).to be true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'did complete' do
|
23
|
+
success = Success.new(nil, nil, 'x')
|
24
|
+
failure = Success.new(nil, [], 'x')
|
25
|
+
|
26
|
+
it 'when there is still input to be consumed, and does not matter if it succeeded or not' do
|
27
|
+
expect(success.completed?).to be false
|
28
|
+
expect(failure.completed?).to be false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require './lib/combinators.rb'
|
2
|
+
require './lib/parser.rb'
|
3
|
+
require './lib/result.rb'
|
4
|
+
|
5
|
+
include RSpec
|
6
|
+
include Parby
|
7
|
+
|
8
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
9
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
10
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
11
|
+
# this file to always be loaded, without a need to explicitly require it in any
|
12
|
+
# files.
|
13
|
+
#
|
14
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
15
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
16
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
17
|
+
# individual file that may not need all of that loaded. Instead, consider making
|
18
|
+
# a separate helper file that requires the additional dependencies and performs
|
19
|
+
# the additional setup, and require it from the spec files that actually need
|
20
|
+
# it.
|
21
|
+
#
|
22
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
23
|
+
RSpec.configure do |config|
|
24
|
+
# rspec-expectations config goes here. You can use an alternate
|
25
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
26
|
+
# assertions if you prefer.
|
27
|
+
config.expect_with :rspec do |expectations|
|
28
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
29
|
+
# and `failure_message` of custom matchers include text for helper methods
|
30
|
+
# defined using `chain`, e.g.:
|
31
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
32
|
+
# # => "be bigger than 2 and smaller than 4"
|
33
|
+
# ...rather than:
|
34
|
+
# # => "be bigger than 2"
|
35
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
36
|
+
end
|
37
|
+
|
38
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
39
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
40
|
+
config.mock_with :rspec do |mocks|
|
41
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
42
|
+
# a real object. This is generally recommended, and will default to
|
43
|
+
# `true` in RSpec 4.
|
44
|
+
mocks.verify_partial_doubles = true
|
45
|
+
end
|
46
|
+
|
47
|
+
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will
|
48
|
+
# have no way to turn it off -- the option exists only for backwards
|
49
|
+
# compatibility in RSpec 3). It causes shared context metadata to be
|
50
|
+
# inherited by the metadata hash of host groups and examples, rather than
|
51
|
+
# triggering implicit auto-inclusion in groups with matching metadata.
|
52
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
53
|
+
|
54
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: parby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rodrigo Martin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-02-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
description:
|
28
|
+
email: rodrigoleonardomartin@gmail.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- lib/combinators.rb
|
34
|
+
- lib/parser.rb
|
35
|
+
- lib/result.rb
|
36
|
+
- spec/combinators_spec.rb
|
37
|
+
- spec/parser_spec.rb
|
38
|
+
- spec/result_spec.rb
|
39
|
+
- spec/spec_helper.rb
|
40
|
+
homepage:
|
41
|
+
licenses:
|
42
|
+
- MIT
|
43
|
+
metadata:
|
44
|
+
source_code_uri: https://github.com/rodr0m4/parby
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
requirements: []
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 2.5.2.3
|
62
|
+
signing_key:
|
63
|
+
specification_version: 4
|
64
|
+
summary: Happy little parser combinators
|
65
|
+
test_files: []
|